From 4875dc6d9539be31dfe2321ccc45f3fb5dc10d3d Mon Sep 17 00:00:00 2001 From: "richmond@google.com" Date: Tue, 12 Aug 2014 23:43:20 +0000 Subject: [PATCH] App Engine Python SDK version 1.9.9 git-svn-id: http://googleappengine.googlecode.com/svn/trunk@457 80f5ef21-4148-0410-bacc-cfb02402ada8 --- python/RELEASE_NOTES | 16 + python/VERSION | 4 +- python/google/appengine/api/appinfo.py | 6 +- python/google/appengine/api/datastore_file_stub.py | 7 +- python/google/appengine/api/mail_stub.py | 2 +- .../appengine/api/search/{stub => }/geo_util.py | 29 +- python/google/appengine/api/search/search.py | 6 +- .../appengine/api/search/stub/document_matcher.py | 2 +- .../api/search/stub/expression_evaluator.py | 4 +- .../search/stub/geo_util.py} | 17 +- python/google/appengine/api/urlfetch.py | 2 +- python/google/appengine/datastore/datastore_pbs.py | 19 +- .../google/appengine/datastore/datastore_query.py | 46 +- .../appengine/datastore/datastore_sqlite_stub.py | 26 +- .../appengine/datastore/datastore_stub_util.py | 111 ++- .../google/appengine/datastore/datastore_v3_pb.py | 57 +- .../google/appengine/datastore/datastore_v4_pb.py | 548 ++++++++++++++- .../appengine/datastore/datastore_v4_stub.py | 112 ++- .../appengine/datastore/datastore_v4_validator.py | 4 +- python/google/appengine/datastore/entity_pb.py | 490 ++++++++++++- .../appengine/ext/appstats/static/appstats_js.js | 175 ++--- .../ext/datastore_admin/backup_handler.py | 3 + .../appengine/ext/datastore_admin/backup_pb2.py | 12 + .../google/appengine/ext/datastore_admin/main.py | 7 +- .../appengine/ext/mapreduce/static/status.js | 21 +- .../google/appengine/tools/adaptive_thread_pool.py | 4 - python/google/appengine/tools/api_server.py | 6 +- .../appengine/tools/app_engine_web_xml_parser.py | 50 +- python/google/appengine/tools/appcfg.py | 7 +- python/google/appengine/tools/appcfg_java.py | 4 +- python/google/appengine/tools/appengine_rpc.py | 4 - .../appengine/tools/appengine_rpc_httplib2.py | 4 - python/google/appengine/tools/boolean_action.py | 4 - python/google/appengine/tools/bulkload_client.py | 3 - python/google/appengine/tools/bulkloader.py | 3 - python/google/appengine/tools/dev-channel-js.js | 46 +- .../appengine/tools/dev_appserver_apiserver.py | 4 - .../appengine/tools/dev_appserver_blobimage.py | 4 - .../appengine/tools/dev_appserver_blobstore.py | 4 - .../appengine/tools/dev_appserver_channel.py | 4 - .../appengine/tools/dev_appserver_import_hook.py | 3 - .../google/appengine/tools/dev_appserver_index.py | 4 - .../google/appengine/tools/dev_appserver_login.py | 3 - .../google/appengine/tools/dev_appserver_main.py | 3 - .../appengine/tools/dev_appserver_multiprocess.py | 2 - .../google/appengine/tools/dev_appserver_oauth.py | 4 - .../google/appengine/tools/dev_appserver_upload.py | 4 - .../appengine/tools/devappserver2/api_server.py | 2 +- .../devappserver2/application_configuration.py | 5 +- .../application_configuration_test.py | 76 +- .../appengine/tools/devappserver2/devappserver2.py | 2 +- .../appengine/tools/devappserver2/http_runtime.py | 11 +- .../tools/devappserver2/http_runtime_test.py | 18 +- .../appengine/tools/devappserver2/php_runtime.py | 3 +- .../tools/devappserver2/python_runtime.py | 3 +- .../tools/devappserver2/runtime_config_pb2.py | 16 + python/google/appengine/tools/devappserver2/tee.py | 1 + .../tools/devappserver2/vm_runtime_proxy.py | 16 +- .../tools/devappserver2/vm_runtime_proxy_dart.py | 7 - .../tools/devappserver2/vm_runtime_proxy_go.py | 207 +++--- python/google/appengine/tools/docker/containers.py | 65 +- python/google/appengine/tools/download_appstats.py | 4 - python/google/appengine/tools/handler_generator.py | 12 +- python/google/appengine/tools/old_dev_appserver.py | 3 - python/google/appengine/tools/os_compat.py | 3 - python/google/appengine/tools/remote_api_shell.py | 4 - python/google/appengine/tools/requeue.py | 4 - .../google/appengine/tools/sdk_update_checker.py | 44 +- python/google/appengine/tools/yaml_translator.py | 15 + python/google/net/proto2/proto/descriptor_pb2.py | 137 ++-- .../google/net/proto2/python/public/descriptor.py | 46 +- python/google/net/proto2/python/public/message.py | 6 +- .../storage/speckle/proto/client_error_code_pb2.py | 2 + python/google/storage/speckle/proto/client_pb2.py | 32 + python/google/storage/speckle/proto/sql_pb2.py | 20 + python/lib/cacerts/urlfetch_cacerts.txt | 28 - .../deprecated_enum}/LICENSE | 0 .../deprecated_enum}/__init__.py | 0 .../deprecated_enum}/test/test_enum.py | 0 .../deprecated_enum}/test/tools.py | 0 python/lib/distutils/distutils/__init__.py | 2 +- python/lib/distutils/distutils/ccompiler.py | 54 +- python/lib/distutils/distutils/command/__init__.py | 33 + python/lib/distutils/distutils/command/bdist.py | 146 ++++ .../lib/distutils/distutils/command/bdist_dumb.py | 133 ++++ .../lib/distutils/distutils/command/bdist_msi.py | 742 ++++++++++++++++++++ .../lib/distutils/distutils/command/bdist_rpm.py | 587 ++++++++++++++++ .../distutils/distutils/command/bdist_wininst.py | 368 ++++++++++ python/lib/distutils/distutils/command/build.py | 147 ++++ .../lib/distutils/distutils/command/build_clib.py | 209 ++++++ .../lib/distutils/distutils/command/build_ext.py | 766 +++++++++++++++++++++ python/lib/distutils/distutils/command/build_py.py | 393 +++++++++++ .../distutils/distutils/command/build_scripts.py | 131 ++++ python/lib/distutils/distutils/command/check.py | 149 ++++ python/lib/distutils/distutils/command/clean.py | 80 +++ python/lib/distutils/distutils/command/config.py | 357 ++++++++++ python/lib/distutils/distutils/command/install.py | 672 ++++++++++++++++++ .../distutils/distutils/command/install_data.py | 81 +++ .../distutils/command/install_egg_info.py | 78 +++ .../distutils/distutils/command/install_headers.py | 51 ++ .../lib/distutils/distutils/command/install_lib.py | 219 ++++++ .../distutils/distutils/command/install_scripts.py | 64 ++ python/lib/distutils/distutils/command/register.py | 315 +++++++++ python/lib/distutils/distutils/command/sdist.py | 477 +++++++++++++ python/lib/distutils/distutils/command/upload.py | 194 ++++++ python/lib/distutils/distutils/config.py | 7 +- python/lib/distutils/distutils/cygwinccompiler.py | 28 +- python/lib/distutils/distutils/dep_util.py | 7 +- python/lib/distutils/distutils/dir_util.py | 4 + python/lib/distutils/distutils/dist.py | 3 +- python/lib/distutils/distutils/filelist.py | 19 +- python/lib/distutils/distutils/msvc9compiler.py | 77 ++- python/lib/distutils/distutils/spawn.py | 28 +- python/lib/distutils/distutils/sysconfig.py | 187 ++--- python/lib/distutils/distutils/unixccompiler.py | 70 +- python/lib/distutils/distutils/util.py | 101 +-- python/lib/grizzled/grizzled/net/ftp/parse.py | 2 +- python/lib/mox/mox_test.py | 2 +- python/lib/protorpc-1.0/protorpc/definition.py | 1 - python/lib/sqlcmd/sqlcmd/__init__.py | 2 +- .../google/appengine/datastore/datastore_v3_pb.php | 63 ++ .../google/appengine/datastore/datastore_v4_pb.php | 442 ++++++++++++ .../sdk/google/appengine/datastore/entity_pb.php | 464 +++++++++++++ .../sdk/google/appengine/runtime/autoloader.php | 5 + python/wrapper_util.py | 2 +- 125 files changed, 9690 insertions(+), 964 deletions(-) rename python/google/appengine/api/search/{stub => }/geo_util.py (69%) copy python/google/appengine/{tools/dev_appserver_index.py => api/search/stub/geo_util.py} (67%) rewrite python/google/appengine/ext/appstats/static/appstats_js.js (71%) rename python/lib/{enum/enum => deprecated_enum/deprecated_enum}/LICENSE (100%) rename python/lib/{enum/enum => deprecated_enum/deprecated_enum}/__init__.py (100%) rename python/lib/{enum/enum => deprecated_enum/deprecated_enum}/test/test_enum.py (100%) rename python/lib/{enum/enum => deprecated_enum/deprecated_enum}/test/tools.py (100%) create mode 100644 python/lib/distutils/distutils/command/__init__.py create mode 100644 python/lib/distutils/distutils/command/bdist.py create mode 100644 python/lib/distutils/distutils/command/bdist_dumb.py create mode 100644 python/lib/distutils/distutils/command/bdist_msi.py create mode 100644 python/lib/distutils/distutils/command/bdist_rpm.py create mode 100644 python/lib/distutils/distutils/command/bdist_wininst.py create mode 100644 python/lib/distutils/distutils/command/build.py create mode 100644 python/lib/distutils/distutils/command/build_clib.py create mode 100644 python/lib/distutils/distutils/command/build_ext.py create mode 100644 python/lib/distutils/distutils/command/build_py.py create mode 100644 python/lib/distutils/distutils/command/build_scripts.py create mode 100644 python/lib/distutils/distutils/command/check.py create mode 100644 python/lib/distutils/distutils/command/clean.py create mode 100644 python/lib/distutils/distutils/command/config.py create mode 100644 python/lib/distutils/distutils/command/install.py create mode 100644 python/lib/distutils/distutils/command/install_data.py create mode 100644 python/lib/distutils/distutils/command/install_egg_info.py create mode 100644 python/lib/distutils/distutils/command/install_headers.py create mode 100644 python/lib/distutils/distutils/command/install_lib.py create mode 100644 python/lib/distutils/distutils/command/install_scripts.py create mode 100644 python/lib/distutils/distutils/command/register.py create mode 100644 python/lib/distutils/distutils/command/sdist.py create mode 100644 python/lib/distutils/distutils/command/upload.py diff --git a/python/RELEASE_NOTES b/python/RELEASE_NOTES index 1944d0bf..f1898339 100644 --- a/python/RELEASE_NOTES +++ b/python/RELEASE_NOTES @@ -3,6 +3,22 @@ All rights reserved. App Engine SDK - Release Notes +Version 1.9.9 + +All +============================== +- TLS is now supported and enabled by default in the dev_appserver. + https://code.google.com/p/googleappengine/issues/detail?id=497 +- Fixed an issue with the Datastore Admin failing to load due to a + DeadlineExceededError when retrieving kinds. + https://code.google.com/p/googleappengine/issues/detail?id=11145 + +PHP +============================= +- Fixed an issue where form fields submitted with Google Cloud Storage upload + may not get encoded correctly. + https://code.google.com/p/googleappengine/issues/detail?id=9534 + Version 1.9.8 All diff --git a/python/VERSION b/python/VERSION index 4f848635..dc3d71b3 100644 --- a/python/VERSION +++ b/python/VERSION @@ -1,5 +1,5 @@ -release: "1.9.8" -timestamp: 1405557678 +release: "1.9.9" +timestamp: 1406573704 api_versions: ['1'] supported_api_versions: python: diff --git a/python/google/appengine/api/appinfo.py b/python/google/appengine/api/appinfo.py index d9d00705..523ea744 100644 --- a/python/google/appengine/api/appinfo.py +++ b/python/google/appengine/api/appinfo.py @@ -1384,13 +1384,9 @@ def VmSafeSetRuntime(appyaml, runtime): appyaml.vm_settings = VmSettings() - appyaml.vm_settings['has_docker_image'] = True - - - if runtime == 'dart' or runtime == 'contrib-dart': runtime = 'dart' - + appyaml.vm_settings['has_docker_image'] = True appyaml.vm_settings['vm_runtime'] = runtime diff --git a/python/google/appengine/api/datastore_file_stub.py b/python/google/appengine/api/datastore_file_stub.py index bc0424df..586a05e3 100644 --- a/python/google/appengine/api/datastore_file_stub.py +++ b/python/google/appengine/api/datastore_file_stub.py @@ -647,7 +647,8 @@ class DatastoreFileStub(datastore_stub_util.BaseDatastore, eg_k = datastore_types.ReferenceToKeyValue(entity_group) return self.__entities_by_group[eg_k].copy() - def _GetQueryCursor(self, query, filters, orders, index_list): + def _GetQueryCursor(self, query, filters, orders, index_list, + filter_predicate=None): app_id = query.app() namespace = query.name_space() @@ -677,8 +678,8 @@ class DatastoreFileStub(datastore_stub_util.BaseDatastore, finally: self.__entities_lock.release() - return datastore_stub_util._ExecuteQuery(results, query, - filters, orders, index_list) + return datastore_stub_util._ExecuteQuery(results, query, filters, orders, + index_list, filter_predicate) def _SetIdCounter(self, id_space, value): """Set the ID counter for id_space to value.""" diff --git a/python/google/appengine/api/mail_stub.py b/python/google/appengine/api/mail_stub.py index 157747ad..63239d25 100644 --- a/python/google/appengine/api/mail_stub.py +++ b/python/google/appengine/api/mail_stub.py @@ -66,7 +66,7 @@ class MailServiceStub(apiproxy_stub.APIProxyStub): enable_sendmail=False, show_mail_body=False, service_name='mail', - allow_tls=False): + allow_tls=True): """Constructor. Args: diff --git a/python/google/appengine/api/search/stub/geo_util.py b/python/google/appengine/api/search/geo_util.py similarity index 69% rename from python/google/appengine/api/search/stub/geo_util.py rename to python/google/appengine/api/search/geo_util.py index fadde470..739ba20f 100644 --- a/python/google/appengine/api/search/stub/geo_util.py +++ b/python/google/appengine/api/search/geo_util.py @@ -18,6 +18,7 @@ import math + class LatLng(object): """A class representing a Latitude/Longitude pair.""" @@ -33,29 +34,39 @@ class LatLng(object): Raises: TypeError: If a non-numeric latitude or longitude is passed. """ - self._lat = math.radians(latitude) - self._lng = math.radians(longitude) + self._lat = latitude + self._lng = longitude @property def latitude(self): """Returns the latitude in degrees.""" - return math.degrees(self._lat) + return self._lat @property def longitude(self): """Returns the longitude in degrees.""" - return math.degrees(self._lng) + return self._lng def __sub__(self, other): """Subtraction. - Returns the great circle distance between two LatLng objects as computed - by the Haversine formula. + Args: + other: the LatLng which this LatLng is subtracted by. + + Returns: + the great circle distance between two LatLng objects as computed + by the Haversine formula. """ assert isinstance(other, LatLng) - dlat = self._lat - other._lat - dlng = self._lng - other._lng + + lat_rad = math.radians(self._lat) + lng_rad = math.radians(self._lng) + other_lat_rad = math.radians(other.latitude) + other_lng_rad = math.radians(other.longitude) + + dlat = lat_rad - other_lat_rad + dlng = lng_rad - other_lng_rad a1 = math.sin(dlat / 2)**2 - a2 = math.cos(self._lat) * math.cos(other._lat) * math.sin(dlng / 2)**2 + a2 = math.cos(lat_rad) * math.cos(other_lat_rad) * math.sin(dlng / 2)**2 return 2 * self._EARTH_RADIUS_METERS * math.asin(math.sqrt(a1 + a2)) diff --git a/python/google/appengine/api/search/search.py b/python/google/appengine/api/search/search.py index 15a9d0cd..57bff9a5 100644 --- a/python/google/appengine/api/search/search.py +++ b/python/google/appengine/api/search/search.py @@ -2479,8 +2479,7 @@ class Index(object): """Index the collection of documents. If any of the documents are already in the index, then reindex them with - their corresponding fresh document. If any of the documents fail to be - indexed, then none of the documents will be indexed. + their corresponding fresh document. Args: documents: A Document or iterable of Documents to index. @@ -2569,8 +2568,7 @@ class Index(object): """Delete the documents with the corresponding document ids from the index. If no document exists for the identifier in the list, then that document - identifier is ignored. If any document delete fails, then no documents - will be deleted. + identifier is ignored. Args: document_ids: A single identifier or list of identifiers of documents diff --git a/python/google/appengine/api/search/stub/document_matcher.py b/python/google/appengine/api/search/stub/document_matcher.py index 56391e49..fae19d64 100644 --- a/python/google/appengine/api/search/stub/document_matcher.py +++ b/python/google/appengine/api/search/stub/document_matcher.py @@ -23,10 +23,10 @@ DocumentMatcher provides an approximation of the Search API's query matching. from google.appengine.datastore import document_pb from google.appengine._internal.antlr3 import tree +from google.appengine.api.search import geo_util from google.appengine.api.search import query_parser from google.appengine.api.search import QueryParser from google.appengine.api.search import search_util -from google.appengine.api.search.stub import geo_util from google.appengine.api.search.stub import simple_tokenizer from google.appengine.api.search.stub import tokens diff --git a/python/google/appengine/api/search/stub/expression_evaluator.py b/python/google/appengine/api/search/stub/expression_evaluator.py index fde4ee83..e0b839b1 100644 --- a/python/google/appengine/api/search/stub/expression_evaluator.py +++ b/python/google/appengine/api/search/stub/expression_evaluator.py @@ -46,15 +46,15 @@ provides an approximation to the API for local testing with dev_appserver. import logging +from google.appengine.datastore import document_pb from google.appengine.api.search import expression_parser from google.appengine.api.search import ExpressionParser +from google.appengine.api.search import geo_util from google.appengine.api.search import query_parser from google.appengine.api.search import search_util -from google.appengine.api.search.stub import geo_util from google.appengine.api.search.stub import simple_tokenizer from google.appengine.api.search.stub import tokens -from google.appengine.datastore import document_pb diff --git a/python/google/appengine/tools/dev_appserver_index.py b/python/google/appengine/api/search/stub/geo_util.py similarity index 67% copy from python/google/appengine/tools/dev_appserver_index.py copy to python/google/appengine/api/search/stub/geo_util.py index a64e7fd4..9ad72a52 100644 --- a/python/google/appengine/tools/dev_appserver_index.py +++ b/python/google/appengine/api/search/stub/geo_util.py @@ -16,18 +16,9 @@ # +import logging +from google.appengine.api.search import * -"""Utilities for generating and updating index.yaml.""" - - - - - - - -from google.appengine.api import apiproxy_stub_map -from google.appengine.datastore.datastore_stub_index import * - -def SetupIndexes(unused_app_id, unused_root_path): - apiproxy_stub_map.apiproxy.GetStub('datastore_v3')._SetupIndexes() +logging.warning('google.appengine.api.search.stub.geo_util is deprecated; ' + 'please use google.appengine.api.search.geo_util') diff --git a/python/google/appengine/api/urlfetch.py b/python/google/appengine/api/urlfetch.py index 50b3aac1..f1a61444 100644 --- a/python/google/appengine/api/urlfetch.py +++ b/python/google/appengine/api/urlfetch.py @@ -399,7 +399,7 @@ def _get_fetch_result(rpc): + url + error_detail) if (err.application_error == urlfetch_service_pb.URLFetchServiceError.INTERNAL_TRANSIENT_ERROR): - raise InteralTransientError( + raise InternalTransientError( 'Temporary error in fetching URL: ' + url + ', please re-try') if (err.application_error == urlfetch_service_pb.URLFetchServiceError.DNS_ERROR): diff --git a/python/google/appengine/datastore/datastore_pbs.py b/python/google/appengine/datastore/datastore_pbs.py index e44b891e..87fe62a0 100644 --- a/python/google/appengine/datastore/datastore_pbs.py +++ b/python/google/appengine/datastore/datastore_pbs.py @@ -332,6 +332,10 @@ class _EntityConverter(object): v3_entity_value = entity_pb.EntityProto() self.v4_to_v3_entity(v4_entity_value, v3_entity_value) v3_value.set_stringvalue(v3_entity_value.SerializePartialToString()) + elif v4_value.has_geo_point_value(): + point_value = v3_value.mutable_pointvalue() + point_value.set_x(v4_value.geo_point_value().latitude()) + point_value.set_y(v4_value.geo_point_value().longitude()) else: pass @@ -425,11 +429,16 @@ class _EntityConverter(object): elif v3_property_value.has_pointvalue(): - self.__v3_to_v4_point_entity(v3_property_value.pointvalue(), - v4_value.mutable_entity_value()) - if v3_meaning != entity_pb.Property.GEORSS_POINT: + if v3_meaning == MEANING_GEORSS_POINT: + point_value = v3_property_value.pointvalue() + v4_value.mutable_geo_point_value().set_latitude(point_value.x()) + v4_value.mutable_geo_point_value().set_longitude(point_value.y()) + else: + self.__v3_to_v4_point_entity(v3_property_value.pointvalue(), + v4_value.mutable_entity_value()) v4_value.set_meaning(MEANING_PREDEFINED_ENTITY_POINT) - v3_meaning = None + + v3_meaning = None elif v3_property_value.has_uservalue(): self.__v3_to_v4_user_entity(v3_property_value.uservalue(), v4_value.mutable_entity_value()) @@ -491,6 +500,8 @@ class _EntityConverter(object): and v4_meaning != MEANING_PREDEFINED_ENTITY_USER): v3_property.set_meaning(entity_pb.Property.ENTITY_PROTO) v4_meaning = None + elif v4_value.has_geo_point_value(): + v3_property.set_meaning(MEANING_GEORSS_POINT) else: pass diff --git a/python/google/appengine/datastore/datastore_query.py b/python/google/appengine/datastore/datastore_query.py index ab9b67da..0c81568c 100644 --- a/python/google/appengine/datastore/datastore_query.py +++ b/python/google/appengine/datastore/datastore_query.py @@ -55,12 +55,14 @@ __all__ = ['Batch', ] import base64 -import pickle import collections +import pickle from google.appengine.datastore import entity_pb + from google.appengine.api import datastore_errors from google.appengine.api import datastore_types +from google.appengine.api.search import geo_util from google.appengine.datastore import datastore_index from google.appengine.datastore import datastore_pb from google.appengine.datastore import datastore_rpc @@ -887,6 +889,48 @@ class _DedupingFilter(_IgnoreFilter): return False +class _BoundingCircleFilter(_SinglePropertyFilter): + """An immutable bounding circle filter for geo locations. + + An immutable filter predicate that constrains a geo location property to a + bounding circle region. The filter is inclusive at the border. The property + has to be of type V3 PointValue. V4 GeoPoints converts to this type. + """ + + + + + + + def __init__(self, property_name, latitude, longitude, radius_meters): + self._property_name = property_name + self._lat_lng = geo_util.LatLng(latitude, longitude) + self._radius_meters = radius_meters + + @classmethod + def _from_v4_pb(cls, bounding_circle_v4_pb): + return _BoundingCircleFilter(bounding_circle_v4_pb.property().name(), + bounding_circle_v4_pb.center().latitude(), + bounding_circle_v4_pb.center().longitude(), + bounding_circle_v4_pb.radius_meters()) + + def _get_prop_name(self): + return self._property_name + + def _apply_to_value(self, value): + + + if value[0] != entity_pb.PropertyValue.kPointValueGroup: + return False + + _, latitude, longitude = value + + lat_lng = geo_util.LatLng(latitude, longitude) + + + return self._lat_lng - lat_lng <= self._radius_meters + + class Order(_PropertyComponent): """A base class that represents a sort order on a query. diff --git a/python/google/appengine/datastore/datastore_sqlite_stub.py b/python/google/appengine/datastore/datastore_sqlite_stub.py index db8ab95e..fc979931 100644 --- a/python/google/appengine/datastore/datastore_sqlite_stub.py +++ b/python/google/appengine/datastore/datastore_sqlite_stub.py @@ -1299,12 +1299,19 @@ class DatastoreSqliteStub(datastore_stub_util.BaseDatastore, self._ReleaseConnection(conn) - def _GetQueryCursor(self, query, filters, orders, index_list): + def _GetQueryCursor(self, query, filters, orders, index_list, + filter_predicate=None): """Returns a query cursor for the provided query. Args: - conn: The SQLite connection. - query: A datastore_pb.Query protobuf. + query: The datastore_pb.Query to run. + filters: A list of filters that override the ones found on query. + orders: A list of orders that override the ones found on query. + index_list: A list of indexes used by the query. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. + Returns: A QueryCursor object. """ @@ -1335,10 +1342,17 @@ class DatastoreSqliteStub(datastore_stub_util.BaseDatastore, conn.execute(sql_stmt, params)) else: db_cursor = _DedupingEntityGenerator(conn.execute(sql_stmt, params)) - dsquery = datastore_stub_util._MakeQuery(query, filters, orders) + dsquery = datastore_stub_util._MakeQuery(query, filters, orders, + filter_predicate) + + filtered_entities = [r for r in db_cursor] + + + if filter_predicate: + filtered_entities = filter(filter_predicate, filtered_entities) + cursor = datastore_stub_util.ListCursor( - query, dsquery, orders, index_list, - [r for r in db_cursor]) + query, dsquery, orders, index_list, filtered_entities) finally: self._ReleaseConnection(conn) return cursor diff --git a/python/google/appengine/datastore/datastore_stub_util.py b/python/google/appengine/datastore/datastore_stub_util.py index be7bc62f..ba264c51 100644 --- a/python/google/appengine/datastore/datastore_stub_util.py +++ b/python/google/appengine/datastore/datastore_stub_util.py @@ -1445,7 +1445,8 @@ class LiveTxn(object): return LoadEntity(entity) @_SynchronizeTxn - def GetQueryCursor(self, query, filters, orders, index_list): + def GetQueryCursor(self, query, filters, orders, index_list, + filter_predicate=None): """Runs the given datastore_pb.Query and returns a QueryCursor for it. Does not see any modifications in the current txn. @@ -1455,6 +1456,9 @@ class LiveTxn(object): filters: A list of filters that override the ones found on query. orders: A list of orders that override the ones found on query. index_list: A list of indexes used by the query. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. Returns: A BaseCursor that can be used to fetch query results. @@ -1462,7 +1466,8 @@ class LiveTxn(object): Check(query.has_ancestor(), 'Query must have an ancestor when performed in a transaction.') snapshot = self._GrabSnapshot(query.ancestor()) - return _ExecuteQuery(snapshot.values(), query, filters, orders, index_list) + return _ExecuteQuery(snapshot.values(), query, filters, orders, index_list, + filter_predicate) @_SynchronizeTxn def Put(self, entity, insert, indexes): @@ -2300,7 +2305,8 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): - def GetQueryCursor(self, raw_query, trusted=False, calling_app=None): + def GetQueryCursor(self, raw_query, trusted=False, calling_app=None, + filter_predicate=None): """Execute a query. Args: @@ -2308,6 +2314,9 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): trusted: If the calling app is trusted. calling_app: The app requesting the results or None to pull the app from the environment. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. Returns: A BaseCursor that can be used to retrieve results. @@ -2325,11 +2334,15 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): CheckQuery(raw_query, filters, orders, self._MAX_QUERY_COMPONENTS) FillUsersInQuery(filters) + index_list = [] - self._CheckHasIndex(raw_query, trusted, calling_app) - index_list = self.__IndexListForQuery(raw_query) + if filter_predicate is None: + self._CheckHasIndex(raw_query, trusted, calling_app) + + + index_list = self.__IndexListForQuery(raw_query) if raw_query.has_transaction(): @@ -2342,11 +2355,13 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): if raw_query.has_ancestor() and raw_query.kind() not in self._pseudo_kinds: txn = self._BeginTransaction(raw_query.app(), False) - return txn.GetQueryCursor(raw_query, filters, orders, index_list) + return txn.GetQueryCursor(raw_query, filters, orders, index_list, + filter_predicate) self.Groom() - return self._GetQueryCursor(raw_query, filters, orders, index_list) + return self._GetQueryCursor(raw_query, filters, orders, index_list, + filter_predicate) def __IndexListForQuery(self, query): """Get the single composite index pb used by the query, if any, as a list. @@ -2664,7 +2679,8 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): """Writes the datastore to disk.""" self.Flush() - def _GetQueryCursor(self, query, filters, orders, index_list): + def _GetQueryCursor(self, query, filters, orders, index_list, + filter_predicate): """Runs the given datastore_pb.Query and returns a QueryCursor for it. This must be implemented by a sub-class. The sub-class does not need to @@ -2675,6 +2691,9 @@ class BaseDatastore(BaseTransactionManager, BaseIndexManager): filters: A list of filters that override the ones found on query. orders: A list of orders that override the ones found on query. index_list: A list of indexes used by the query. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. Returns: A BaseCursor that can be used to fetch query results. @@ -2949,9 +2968,10 @@ class DatastoreStub(object): self._datastore.Touch(req.key_list(), self._trusted, self._app_id) @_NeedsIndexes - def _Dynamic_RunQuery(self, query, query_result): + def _Dynamic_RunQuery(self, query, query_result, filter_predicate=None): self.__UpgradeCursors(query) - cursor = self._datastore.GetQueryCursor(query, self._trusted, self._app_id) + cursor = self._datastore.GetQueryCursor(query, self._trusted, self._app_id, + filter_predicate) if query.has_count(): count = query.count() @@ -3401,6 +3421,12 @@ class StubQueryConverter(object): v4_filter: a datastore_v4_pb.Filter v3_query: a datastore_pb.Query to populate with filters """ + + datastore_pbs.check_conversion(not v4_filter.has_bounding_circle_filter(), + 'bounding circle filter not supported') + datastore_pbs.check_conversion(not v4_filter.has_bounding_box_filter(), + 'bounding box filter not supported') + if v4_filter.has_property_filter(): v4_property_filter = v4_filter.property_filter() if (v4_property_filter.operator() @@ -4018,17 +4044,56 @@ def _GuessOrders(filters, orders): return orders -def _MakeQuery(query, filters, orders): +def _MakeQuery(query_pb, filters, orders, filter_predicate): """Make a datastore_query.Query for the given datastore_pb.Query. - Overrides filters and orders in query with the specified arguments.""" - clone = datastore_pb.Query() - clone.CopyFrom(query) - clone.clear_filter() - clone.clear_order() - clone.filter_list().extend(filters) - clone.order_list().extend(orders) - return datastore_query.Query._from_pb(clone) + Overrides filters and orders in query with the specified arguments. + + Args: + query_pb: a datastore_pb.Query. + filters: the filters from query. + orders: the orders from query. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. + + Returns: + A datastore_query.Query for the datastore_pb.Query.""" + + + + + + clone_pb = datastore_pb.Query() + clone_pb.CopyFrom(query_pb) + clone_pb.clear_filter() + clone_pb.clear_order() + clone_pb.filter_list().extend(filters) + clone_pb.order_list().extend(orders) + + query = datastore_query.Query._from_pb(clone_pb) + + assert datastore_v4_pb.CompositeFilter._Operator_NAMES.values() == ['AND'] + + + + + if filter_predicate is not None: + if query.filter_predicate is not None: + + + filter_predicate = datastore_query.CompositeFilter( + datastore_query.CompositeFilter.AND, + [filter_predicate, query.filter_predicate]) + + return datastore_query.Query(app=query.app, + namespace=query.namespace, + ancestor=query.ancestor, + filter_predicate=filter_predicate, + group_by=query.group_by, + order=query.order) + else: + return query def _CreateIndexEntities(entity, postfix_props): """Creates entities for index values that would appear in prodcution. @@ -4112,7 +4177,8 @@ def _CreateIndexOnlyQueryResults(results, postfix_props): return new_results -def _ExecuteQuery(results, query, filters, orders, index_list): +def _ExecuteQuery(results, query, filters, orders, index_list, + filter_predicate=None): """Executes the query on a superset of its results. Args: @@ -4121,12 +4187,15 @@ def _ExecuteQuery(results, query, filters, orders, index_list): filters: the filters from query. orders: the orders from query. index_list: the list of indexes used by the query. + filter_predicate: an additional filter of type + datastore_query.FilterPredicate. This is passed along to implement V4 + specific filters without changing the entire stub. Returns: A ListCursor over the results of applying query to results. """ orders = _GuessOrders(filters, orders) - dsquery = _MakeQuery(query, filters, orders) + dsquery = _MakeQuery(query, filters, orders, filter_predicate) if query.property_name_size(): results = _CreateIndexOnlyQueryResults( diff --git a/python/google/appengine/datastore/datastore_v3_pb.py b/python/google/appengine/datastore/datastore_v3_pb.py index 25eb7d9f..56312bda 100644 --- a/python/google/appengine/datastore/datastore_v3_pb.py +++ b/python/google/appengine/datastore/datastore_v3_pb.py @@ -6046,6 +6046,7 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): def __init__(self, contents=None): self.key_ = [] + self.composite_index_ = [] self.snapshot_ = [] self.lazy_init_lock_ = thread.allocate_lock() if contents is not None: self.MergeFromString(contents) @@ -6104,6 +6105,22 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): def has_transaction(self): return self.has_transaction_ + def composite_index_size(self): return len(self.composite_index_) + def composite_index_list(self): return self.composite_index_ + + def composite_index(self, i): + return self.composite_index_[i] + + def mutable_composite_index(self, i): + return self.composite_index_[i] + + def add_composite_index(self): + x = CompositeIndex() + self.composite_index_.append(x) + return x + + def clear_composite_index(self): + self.composite_index_ = [] def trusted(self): return self.trusted_ def set_trusted(self, x): @@ -6165,6 +6182,7 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): if (x.has_header()): self.mutable_header().MergeFrom(x.header()) for i in xrange(x.key_size()): self.add_key().CopyFrom(x.key(i)) if (x.has_transaction()): self.mutable_transaction().MergeFrom(x.transaction()) + for i in xrange(x.composite_index_size()): self.add_composite_index().CopyFrom(x.composite_index(i)) if (x.has_trusted()): self.set_trusted(x.trusted()) if (x.has_force()): self.set_force(x.force()) if (x.has_mark_changes()): self.set_mark_changes(x.mark_changes()) @@ -6179,6 +6197,9 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): if e1 != e2: return 0 if self.has_transaction_ != x.has_transaction_: return 0 if self.has_transaction_ and self.transaction_ != x.transaction_: return 0 + if len(self.composite_index_) != len(x.composite_index_): return 0 + for e1, e2 in zip(self.composite_index_, x.composite_index_): + if e1 != e2: return 0 if self.has_trusted_ != x.has_trusted_: return 0 if self.has_trusted_ and self.trusted_ != x.trusted_: return 0 if self.has_force_ != x.has_force_: return 0 @@ -6196,6 +6217,8 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): 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 + for p in self.composite_index_: + if not p.IsInitialized(debug_strs): initialized=0 for p in self.snapshot_: if not p.IsInitialized(debug_strs): initialized=0 return initialized @@ -6206,6 +6229,8 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): n += 1 * len(self.key_) for i in xrange(len(self.key_)): n += self.lengthString(self.key_[i].ByteSize()) if (self.has_transaction_): n += 1 + self.lengthString(self.transaction_.ByteSize()) + n += 1 * len(self.composite_index_) + for i in xrange(len(self.composite_index_)): n += self.lengthString(self.composite_index_[i].ByteSize()) if (self.has_trusted_): n += 2 if (self.has_force_): n += 2 if (self.has_mark_changes_): n += 2 @@ -6219,6 +6244,8 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): n += 1 * len(self.key_) for i in xrange(len(self.key_)): n += self.lengthString(self.key_[i].ByteSizePartial()) if (self.has_transaction_): n += 1 + self.lengthString(self.transaction_.ByteSizePartial()) + n += 1 * len(self.composite_index_) + for i in xrange(len(self.composite_index_)): n += self.lengthString(self.composite_index_[i].ByteSizePartial()) if (self.has_trusted_): n += 2 if (self.has_force_): n += 2 if (self.has_mark_changes_): n += 2 @@ -6230,6 +6257,7 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): self.clear_header() self.clear_key() self.clear_transaction() + self.clear_composite_index() self.clear_trusted() self.clear_force() self.clear_mark_changes() @@ -6261,6 +6289,10 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): out.putVarInt32(82) out.putVarInt32(self.header_.ByteSize()) self.header_.OutputUnchecked(out) + for i in xrange(len(self.composite_index_)): + out.putVarInt32(90) + out.putVarInt32(self.composite_index_[i].ByteSize()) + self.composite_index_[i].OutputUnchecked(out) def OutputPartial(self, out): if (self.has_trusted_): @@ -6288,6 +6320,10 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): out.putVarInt32(82) out.putVarInt32(self.header_.ByteSizePartial()) self.header_.OutputPartial(out) + for i in xrange(len(self.composite_index_)): + out.putVarInt32(90) + out.putVarInt32(self.composite_index_[i].ByteSizePartial()) + self.composite_index_[i].OutputPartial(out) def TryMerge(self, d): while d.avail() > 0: @@ -6325,6 +6361,12 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): d.skip(length) self.mutable_header().TryMerge(tmp) continue + if tt == 90: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.add_composite_index().TryMerge(tmp) + continue if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError @@ -6349,6 +6391,14 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): res+=prefix+"transaction <\n" res+=self.transaction_.__str__(prefix + " ", printElemNumber) res+=prefix+">\n" + cnt=0 + for e in self.composite_index_: + elm="" + if printElemNumber: elm="(%d)" % cnt + res+=prefix+("composite_index%s <\n" % elm) + res+=e.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + cnt+=1 if self.has_trusted_: res+=prefix+("trusted: %s\n" % self.DebugFormatBool(self.trusted_)) if self.has_force_: res+=prefix+("force: %s\n" % self.DebugFormatBool(self.force_)) if self.has_mark_changes_: res+=prefix+("mark_changes: %s\n" % self.DebugFormatBool(self.mark_changes_)) @@ -6369,6 +6419,7 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): kheader = 10 kkey = 6 ktransaction = 5 + kcomposite_index = 11 ktrusted = 4 kforce = 7 kmark_changes = 8 @@ -6383,7 +6434,8 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): 8: "mark_changes", 9: "snapshot", 10: "header", - }, 10) + 11: "composite_index", + }, 11) _TYPES = _BuildTagLookupTable({ 0: ProtocolBuffer.Encoder.NUMERIC, @@ -6394,7 +6446,8 @@ class DeleteRequest(ProtocolBuffer.ProtocolMessage): 8: ProtocolBuffer.Encoder.NUMERIC, 9: ProtocolBuffer.Encoder.STRING, 10: ProtocolBuffer.Encoder.STRING, - }, 10, ProtocolBuffer.Encoder.MAX_TYPE) + 11: ProtocolBuffer.Encoder.STRING, + }, 11, ProtocolBuffer.Encoder.MAX_TYPE) _STYLE = """""" diff --git a/python/google/appengine/datastore/datastore_v4_pb.py b/python/google/appengine/datastore/datastore_v4_pb.py index 424303df..ba11407a 100644 --- a/python/google/appengine/datastore/datastore_v4_pb.py +++ b/python/google/appengine/datastore/datastore_v4_pb.py @@ -177,7 +177,7 @@ class Error(ProtocolBuffer.ProtocolMessage): _STYLE_CONTENT_TYPE = """""" _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.Error' _SERIALIZED_DESCRIPTOR = array.array('B') - _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHWFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVycm9yc3oJRXJyb3JDb2RliwGSAQtCQURfUkVRVUVTVJgBAYwBiwGSARZDT05DVVJSRU5UX1RSQU5TQUNUSU9OmAECjAGLAZIBDklOVEVSTkFMX0VSUk9SmAEDjAGLAZIBCk5FRURfSU5ERViYAQSMAYsBkgEHVElNRU9VVJgBBYwBiwGSARFQRVJNSVNTSU9OX0RFTklFRJgBBowBiwGSAQ5CSUdUQUJMRV9FUlJPUpgBB4wBiwGSARxDT01NSVRURURfQlVUX1NUSUxMX0FQUExZSU5HmAEIjAGLAZIBE0NBUEFCSUxJVFlfRElTQUJMRUSYAQmMAYsBkgEVVFJZX0FMVEVSTkFURV9CQUNLRU5EmAEKjAGLAZIBEVNBRkVfVElNRV9UT09fT0xEmAELjAF0ugHoMgonYXBwaG9zdGluZy9kYXRhc3RvcmUvZGF0YXN0b3JlX3Y0LnByb3RvEhdhcHBob3N0aW5nLmRhdGFzdG9yZS52NBokYXBwaG9zdGluZy9kYXRhc3RvcmUvZW50aXR5X3Y0LnByb3RvIosCCgVFcnJvciKBAgoJRXJyb3JDb2RlEg8KC0JBRF9SRVFVRVNUEAESGgoWQ09OQ1VSUkVOVF9UUkFOU0FDVElPThACEhIKDklOVEVSTkFMX0VSUk9SEAMSDgoKTkVFRF9JTkRFWBAEEgsKB1RJTUVPVVQQBRIVChFQRVJNSVNTSU9OX0RFTklFRBAGEhIKDkJJR1RBQkxFX0VSUk9SEAcSIAocQ09NTUlUVEVEX0JVVF9TVElMTF9BUFBMWUlORxAIEhcKE0NBUEFCSUxJVFlfRElTQUJMRUQQCRIZChVUUllfQUxURVJOQVRFX0JBQ0tFTkQQChIVChFTQUZFX1RJTUVfVE9PX09MRBALIoYBCgxFbnRpdHlSZXN1bHQSLwoGZW50aXR5GAEgAigLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Eg8KB3ZlcnNpb24YAiABKAMiNAoKUmVzdWx0VHlwZRIICgRGVUxMEAESDgoKUFJPSkVDVElPThACEgwKCEtFWV9PTkxZEAMi8QIKBVF1ZXJ5Ej8KCnByb2plY3Rpb24YAiADKAsyKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUV4cHJlc3Npb24SNQoEa2luZBgDIAMoCzInLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktpbmRFeHByZXNzaW9uEi8KBmZpbHRlchgEIAEoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlchI1CgVvcmRlchgFIAMoCzImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXISPAoIZ3JvdXBfYnkYBiADKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRIUCgxzdGFydF9jdXJzb3IYByABKAwSEgoKZW5kX2N1cnNvchgIIAEoDBIRCgZvZmZzZXQYCiABKAU6ATASDQoFbGltaXQYCyABKAUiHgoOS2luZEV4cHJlc3Npb24SDAoEbmFtZRgBIAIoCSIhChFQcm9wZXJ0eVJlZmVyZW5jZRIMCgRuYW1lGAIgAigJItMBChJQcm9wZXJ0eUV4cHJlc3Npb24SPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRJdChRhZ2dyZWdhdGlvbl9mdW5jdGlvbhgCIAEoDjI/LmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RXhwcmVzc2lvbi5BZ2dyZWdhdGlvbkZ1bmN0aW9uIiAKE0FnZ3JlZ2F0aW9uRnVuY3Rpb24SCQoFRklSU1QQASLJAQoNUHJvcGVydHlPcmRlchI8Cghwcm9wZXJ0eRgBIAIoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEk4KCWRpcmVjdGlvbhgCIAEoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXIuRGlyZWN0aW9uOglBU0NFTkRJTkciKgoJRGlyZWN0aW9uEg0KCUFTQ0VORElORxABEg4KCkRFU0NFTkRJTkcQAiKOAQoGRmlsdGVyEkIKEGNvbXBvc2l0ZV9maWx0ZXIYASABKAsyKC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21wb3NpdGVGaWx0ZXISQAoPcHJvcGVydHlfZmlsdGVyGAIgASgLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlGaWx0ZXIinAEKD0NvbXBvc2l0ZUZpbHRlchJDCghvcGVyYXRvchgBIAIoDjIxLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbXBvc2l0ZUZpbHRlci5PcGVyYXRvchIvCgZmaWx0ZXIYAiADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5GaWx0ZXIiEwoIT3BlcmF0b3ISBwoDQU5EEAEivgIKDlByb3BlcnR5RmlsdGVyEjwKCHByb3BlcnR5GAEgAigLMiouYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlSZWZlcmVuY2USQgoIb3BlcmF0b3IYAiACKA4yMC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUZpbHRlci5PcGVyYXRvchItCgV2YWx1ZRgDIAIoCzIeLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlZhbHVlInsKCE9wZXJhdG9yEg0KCUxFU1NfVEhBThABEhYKEkxFU1NfVEhBTl9PUl9FUVVBTBACEhAKDEdSRUFURVJfVEhBThADEhkKFUdSRUFURVJfVEhBTl9PUl9FUVVBTBAEEgkKBUVRVUFMEAUSEAoMSEFTX0FOQ0VTVE9SEAsisAEKCEdxbFF1ZXJ5EhQKDHF1ZXJ5X3N0cmluZxgBIAIoCRIcCg1hbGxvd19saXRlcmFsGAIgASgIOgVmYWxzZRI2CghuYW1lX2FyZxgDIAMoCzIkLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkdxbFF1ZXJ5QXJnEjgKCm51bWJlcl9hcmcYBCADKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeUFyZyJaCgtHcWxRdWVyeUFyZxIMCgRuYW1lGAEgASgJEi0KBXZhbHVlGAIgASgLMh4uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuVmFsdWUSDgoGY3Vyc29yGAMgASgMIpEDChBRdWVyeVJlc3VsdEJhdGNoEkwKEmVudGl0eV9yZXN1bHRfdHlwZRgBIAIoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdC5SZXN1bHRUeXBlEjwKDWVudGl0eV9yZXN1bHQYAiADKAsyJS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHlSZXN1bHQSEgoKZW5kX2N1cnNvchgEIAEoDBJPCgxtb3JlX3Jlc3VsdHMYBSACKA4yOS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeVJlc3VsdEJhdGNoLk1vcmVSZXN1bHRzVHlwZRIaCg9za2lwcGVkX3Jlc3VsdHMYBiABKAU6ATASGAoQc25hcHNob3RfdmVyc2lvbhgHIAEoAyJWCg9Nb3JlUmVzdWx0c1R5cGUSEAoMTk9UX0ZJTklTSEVEEAESHAoYTU9SRV9SRVNVTFRTX0FGVEVSX0xJTUlUEAISEwoPTk9fTU9SRV9SRVNVTFRTEAMi8gEKCE11dGF0aW9uEkAKAm9wGAEgASgOMisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb24uT3BlcmF0aW9uOgdVTktOT1dOEikKA2tleRgCIAEoCzIcLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktleRIvCgZlbnRpdHkYAyABKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkiSAoJT3BlcmF0aW9uEgsKB1VOS05PV04QABIKCgZJTlNFUlQQARIKCgZVUERBVEUQAhIKCgZVUFNFUlQQAxIKCgZERUxFVEUQBCJTCg5NdXRhdGlvblJlc3VsdBIpCgNrZXkYAyABKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSFgoLbmV3X3ZlcnNpb24YBCABKAM6ATAipAIKEkRlcHJlY2F0ZWRNdXRhdGlvbhIvCgZ1cHNlcnQYASADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSLwoGdXBkYXRlGAIgAygLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Ei8KBmluc2VydBgDIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRI3Cg5pbnNlcnRfYXV0b19pZBgEIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRIsCgZkZWxldGUYBSADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSFAoFZm9yY2UYBiABKAg6BWZhbHNlIusBChhEZXByZWNhdGVkTXV0YXRpb25SZXN1bHQSFQoNaW5kZXhfdXBkYXRlcxgBIAIoBRI4ChJpbnNlcnRfYXV0b19pZF9rZXkYAiADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSFgoOdXBzZXJ0X3ZlcnNpb24YAyADKAMSFgoOdXBkYXRlX3ZlcnNpb24YBCADKAMSFgoOaW5zZXJ0X3ZlcnNpb24YBSADKAMSHgoWaW5zZXJ0X2F1dG9faWRfdmVyc2lvbhgGIAMoAxIWCg5kZWxldGVfdmVyc2lvbhgHIAMoAyK1AQoLUmVhZE9wdGlvbnMSVwoQcmVhZF9jb25zaXN0ZW5jeRgBIAEoDjI0LmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJlYWRPcHRpb25zLlJlYWRDb25zaXN0ZW5jeToHREVGQVVMVBITCgt0cmFuc2FjdGlvbhgCIAEoDCI4Cg9SZWFkQ29uc2lzdGVuY3kSCwoHREVGQVVMVBAAEgoKBlNUUk9ORxABEgwKCEVWRU5UVUFMEAIidgoNTG9va3VwUmVxdWVzdBI6CgxyZWFkX29wdGlvbnMYASABKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SZWFkT3B0aW9ucxIpCgNrZXkYAyADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkirgEKDkxvb2t1cFJlc3BvbnNlEjQKBWZvdW5kGAEgAygLMiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0EjYKB21pc3NpbmcYAiADKAsyJS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHlSZXN1bHQSLgoIZGVmZXJyZWQYAyADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkiqwIKD1J1blF1ZXJ5UmVxdWVzdBI6CgxyZWFkX29wdGlvbnMYASABKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SZWFkT3B0aW9ucxI6CgxwYXJ0aXRpb25faWQYAiABKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5QYXJ0aXRpb25JZBItCgVxdWVyeRgDIAEoCzIeLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5EjQKCWdxbF9xdWVyeRgHIAEoCzIhLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkdxbFF1ZXJ5Eh0KFW1pbl9zYWZlX3RpbWVfc2Vjb25kcxgEIAEoAxIcChRzdWdnZXN0ZWRfYmF0Y2hfc2l6ZRgFIAEoBSJiChBSdW5RdWVyeVJlc3BvbnNlEjgKBWJhdGNoGAEgAigLMikuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUXVlcnlSZXN1bHRCYXRjaBIUCgxxdWVyeV9oYW5kbGUYAiABKAwiLAoUQ29udGludWVRdWVyeVJlcXVlc3QSFAoMcXVlcnlfaGFuZGxlGAEgAigMIlEKFUNvbnRpbnVlUXVlcnlSZXNwb25zZRI4CgViYXRjaBgBIAIoCzIpLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5UmVzdWx0QmF0Y2giUwoXQmVnaW5UcmFuc2FjdGlvblJlcXVlc3QSGgoLY3Jvc3NfZ3JvdXAYASABKAg6BWZhbHNlEhwKDWNyb3NzX3JlcXVlc3QYAiABKAg6BWZhbHNlIi8KGEJlZ2luVHJhbnNhY3Rpb25SZXNwb25zZRITCgt0cmFuc2FjdGlvbhgBIAIoDCImCg9Sb2xsYmFja1JlcXVlc3QSEwoLdHJhbnNhY3Rpb24YASACKAwiEgoQUm9sbGJhY2tSZXNwb25zZSLAAgoNQ29tbWl0UmVxdWVzdBITCgt0cmFuc2FjdGlvbhgBIAEoDBIzCghtdXRhdGlvbhgFIAMoCzIhLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uEkgKE2RlcHJlY2F0ZWRfbXV0YXRpb24YAiABKAsyKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5EZXByZWNhdGVkTXV0YXRpb24SSAoEbW9kZRgEIAEoDjIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbW1pdFJlcXVlc3QuTW9kZToNVFJBTlNBQ1RJT05BTBIfChBpZ25vcmVfcmVhZF9vbmx5GAYgASgIOgVmYWxzZSIwCgRNb2RlEhEKDVRSQU5TQUNUSU9OQUwQARIVChFOT05fVFJBTlNBQ1RJT05BTBACIsABCg5Db21taXRSZXNwb25zZRJACg9tdXRhdGlvbl9yZXN1bHQYAyADKAsyJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5NdXRhdGlvblJlc3VsdBJVChpkZXByZWNhdGVkX211dGF0aW9uX3Jlc3VsdBgBIAEoCzIxLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkRlcHJlY2F0ZWRNdXRhdGlvblJlc3VsdBIVCg1pbmRleF91cGRhdGVzGAQgASgFInMKEkFsbG9jYXRlSWRzUmVxdWVzdBIuCghhbGxvY2F0ZRgBIAMoCzIcLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktleRItCgdyZXNlcnZlGAIgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5IkYKE0FsbG9jYXRlSWRzUmVzcG9uc2USLwoJYWxsb2NhdGVkGAEgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5IlgKDFdyaXRlUmVxdWVzdBJIChNkZXByZWNhdGVkX211dGF0aW9uGAEgAigLMisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRGVwcmVjYXRlZE11dGF0aW9uMqIHChJEYXRhc3RvcmVWNFNlcnZpY2USeQoQQmVnaW5UcmFuc2FjdGlvbhIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJlZ2luVHJhbnNhY3Rpb25SZXF1ZXN0GjEuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQmVnaW5UcmFuc2FjdGlvblJlc3BvbnNlIgASYQoIUm9sbGJhY2sSKC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Sb2xsYmFja1JlcXVlc3QaKS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Sb2xsYmFja1Jlc3BvbnNlIgASWwoGQ29tbWl0EiYuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVxdWVzdBonLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbW1pdFJlc3BvbnNlIgASYQoIUnVuUXVlcnkSKC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SdW5RdWVyeVJlcXVlc3QaKS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SdW5RdWVyeVJlc3BvbnNlIgAScAoNQ29udGludWVRdWVyeRItLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbnRpbnVlUXVlcnlSZXF1ZXN0Gi4uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29udGludWVRdWVyeVJlc3BvbnNlIgASWwoGTG9va3VwEiYuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVxdWVzdBonLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkxvb2t1cFJlc3BvbnNlIgASagoLQWxsb2NhdGVJZHMSKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5BbGxvY2F0ZUlkc1JlcXVlc3QaLC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5BbGxvY2F0ZUlkc1Jlc3BvbnNlIgASWAoDR2V0EiYuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVxdWVzdBonLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkxvb2t1cFJlc3BvbnNlIgASWQoFV3JpdGUSJS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Xcml0ZVJlcXVlc3QaJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21taXRSZXNwb25zZSIAQiMKH2NvbS5nb29nbGUuYXBwaG9zdGluZy5kYXRhc3RvcmUgAQ==")) + _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHWFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVycm9yc3oJRXJyb3JDb2RliwGSAQtCQURfUkVRVUVTVJgBAYwBiwGSARZDT05DVVJSRU5UX1RSQU5TQUNUSU9OmAECjAGLAZIBDklOVEVSTkFMX0VSUk9SmAEDjAGLAZIBCk5FRURfSU5ERViYAQSMAYsBkgEHVElNRU9VVJgBBYwBiwGSARFQRVJNSVNTSU9OX0RFTklFRJgBBowBiwGSAQ5CSUdUQUJMRV9FUlJPUpgBB4wBiwGSARxDT01NSVRURURfQlVUX1NUSUxMX0FQUExZSU5HmAEIjAGLAZIBE0NBUEFCSUxJVFlfRElTQUJMRUSYAQmMAYsBkgEVVFJZX0FMVEVSTkFURV9CQUNLRU5EmAEKjAGLAZIBEVNBRkVfVElNRV9UT09fT0xEmAELjAF0ugHhNgonYXBwaG9zdGluZy9kYXRhc3RvcmUvZGF0YXN0b3JlX3Y0LnByb3RvEhdhcHBob3N0aW5nLmRhdGFzdG9yZS52NBokYXBwaG9zdGluZy9kYXRhc3RvcmUvZW50aXR5X3Y0LnByb3RvIosCCgVFcnJvciKBAgoJRXJyb3JDb2RlEg8KC0JBRF9SRVFVRVNUEAESGgoWQ09OQ1VSUkVOVF9UUkFOU0FDVElPThACEhIKDklOVEVSTkFMX0VSUk9SEAMSDgoKTkVFRF9JTkRFWBAEEgsKB1RJTUVPVVQQBRIVChFQRVJNSVNTSU9OX0RFTklFRBAGEhIKDkJJR1RBQkxFX0VSUk9SEAcSIAocQ09NTUlUVEVEX0JVVF9TVElMTF9BUFBMWUlORxAIEhcKE0NBUEFCSUxJVFlfRElTQUJMRUQQCRIZChVUUllfQUxURVJOQVRFX0JBQ0tFTkQQChIVChFTQUZFX1RJTUVfVE9PX09MRBALIoYBCgxFbnRpdHlSZXN1bHQSLwoGZW50aXR5GAEgAigLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Eg8KB3ZlcnNpb24YAiABKAMiNAoKUmVzdWx0VHlwZRIICgRGVUxMEAESDgoKUFJPSkVDVElPThACEgwKCEtFWV9PTkxZEAMi8QIKBVF1ZXJ5Ej8KCnByb2plY3Rpb24YAiADKAsyKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUV4cHJlc3Npb24SNQoEa2luZBgDIAMoCzInLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktpbmRFeHByZXNzaW9uEi8KBmZpbHRlchgEIAEoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlchI1CgVvcmRlchgFIAMoCzImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXISPAoIZ3JvdXBfYnkYBiADKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRIUCgxzdGFydF9jdXJzb3IYByABKAwSEgoKZW5kX2N1cnNvchgIIAEoDBIRCgZvZmZzZXQYCiABKAU6ATASDQoFbGltaXQYCyABKAUiHgoOS2luZEV4cHJlc3Npb24SDAoEbmFtZRgBIAIoCSIhChFQcm9wZXJ0eVJlZmVyZW5jZRIMCgRuYW1lGAIgAigJItMBChJQcm9wZXJ0eUV4cHJlc3Npb24SPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRJdChRhZ2dyZWdhdGlvbl9mdW5jdGlvbhgCIAEoDjI/LmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RXhwcmVzc2lvbi5BZ2dyZWdhdGlvbkZ1bmN0aW9uIiAKE0FnZ3JlZ2F0aW9uRnVuY3Rpb24SCQoFRklSU1QQASLJAQoNUHJvcGVydHlPcmRlchI8Cghwcm9wZXJ0eRgBIAIoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEk4KCWRpcmVjdGlvbhgCIAEoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXIuRGlyZWN0aW9uOglBU0NFTkRJTkciKgoJRGlyZWN0aW9uEg0KCUFTQ0VORElORxABEg4KCkRFU0NFTkRJTkcQAiKmAgoGRmlsdGVyEkIKEGNvbXBvc2l0ZV9maWx0ZXIYASABKAsyKC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21wb3NpdGVGaWx0ZXISQAoPcHJvcGVydHlfZmlsdGVyGAIgASgLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlGaWx0ZXISTQoWYm91bmRpbmdfY2lyY2xlX2ZpbHRlchgDIAEoCzItLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJvdW5kaW5nQ2lyY2xlRmlsdGVyEkcKE2JvdW5kaW5nX2JveF9maWx0ZXIYBCABKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Cb3VuZGluZ0JveEZpbHRlciKcAQoPQ29tcG9zaXRlRmlsdGVyEkMKCG9wZXJhdG9yGAEgAigOMjEuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tcG9zaXRlRmlsdGVyLk9wZXJhdG9yEi8KBmZpbHRlchgCIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlciITCghPcGVyYXRvchIHCgNBTkQQASK+AgoOUHJvcGVydHlGaWx0ZXISPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRJCCghvcGVyYXRvchgCIAIoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RmlsdGVyLk9wZXJhdG9yEi0KBXZhbHVlGAMgAigLMh4uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuVmFsdWUiewoIT3BlcmF0b3ISDQoJTEVTU19USEFOEAESFgoSTEVTU19USEFOX09SX0VRVUFMEAISEAoMR1JFQVRFUl9USEFOEAMSGQoVR1JFQVRFUl9USEFOX09SX0VRVUFMEAQSCQoFRVFVQUwQBRIQCgxIQVNfQU5DRVNUT1IQCyKeAQoUQm91bmRpbmdDaXJjbGVGaWx0ZXISPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRIxCgZjZW50ZXIYAiACKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HZW9Qb2ludBIVCg1yYWRpdXNfbWV0ZXJzGAMgAigBIr0BChFCb3VuZGluZ0JveEZpbHRlchI8Cghwcm9wZXJ0eRgBIAIoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEjQKCXNvdXRod2VzdBgCIAIoCzIhLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkdlb1BvaW50EjQKCW5vcnRoZWFzdBgDIAIoCzIhLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkdlb1BvaW50IrABCghHcWxRdWVyeRIUCgxxdWVyeV9zdHJpbmcYASACKAkSHAoNYWxsb3dfbGl0ZXJhbBgCIAEoCDoFZmFsc2USNgoIbmFtZV9hcmcYAyADKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeUFyZxI4CgpudW1iZXJfYXJnGAQgAygLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuR3FsUXVlcnlBcmciWgoLR3FsUXVlcnlBcmcSDAoEbmFtZRgBIAEoCRItCgV2YWx1ZRgCIAEoCzIeLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlZhbHVlEg4KBmN1cnNvchgDIAEoDCKRAwoQUXVlcnlSZXN1bHRCYXRjaBJMChJlbnRpdHlfcmVzdWx0X3R5cGUYASACKA4yMC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHlSZXN1bHQuUmVzdWx0VHlwZRI8Cg1lbnRpdHlfcmVzdWx0GAIgAygLMiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0EhIKCmVuZF9jdXJzb3IYBCABKAwSTwoMbW9yZV9yZXN1bHRzGAUgAigOMjkuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUXVlcnlSZXN1bHRCYXRjaC5Nb3JlUmVzdWx0c1R5cGUSGgoPc2tpcHBlZF9yZXN1bHRzGAYgASgFOgEwEhgKEHNuYXBzaG90X3ZlcnNpb24YByABKAMiVgoPTW9yZVJlc3VsdHNUeXBlEhAKDE5PVF9GSU5JU0hFRBABEhwKGE1PUkVfUkVTVUxUU19BRlRFUl9MSU1JVBACEhMKD05PX01PUkVfUkVTVUxUUxADIvIBCghNdXRhdGlvbhJACgJvcBgBIAEoDjIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uLk9wZXJhdGlvbjoHVU5LTk9XThIpCgNrZXkYAiABKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSLwoGZW50aXR5GAMgASgLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5IkgKCU9wZXJhdGlvbhILCgdVTktOT1dOEAASCgoGSU5TRVJUEAESCgoGVVBEQVRFEAISCgoGVVBTRVJUEAMSCgoGREVMRVRFEAQiUwoOTXV0YXRpb25SZXN1bHQSKQoDa2V5GAMgASgLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5EhYKC25ld192ZXJzaW9uGAQgASgDOgEwIqQCChJEZXByZWNhdGVkTXV0YXRpb24SLwoGdXBzZXJ0GAEgAygLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Ei8KBnVwZGF0ZRgCIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRIvCgZpbnNlcnQYAyADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSNwoOaW5zZXJ0X2F1dG9faWQYBCADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSLAoGZGVsZXRlGAUgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5EhQKBWZvcmNlGAYgASgIOgVmYWxzZSLrAQoYRGVwcmVjYXRlZE11dGF0aW9uUmVzdWx0EhUKDWluZGV4X3VwZGF0ZXMYASACKAUSOAoSaW5zZXJ0X2F1dG9faWRfa2V5GAIgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5EhYKDnVwc2VydF92ZXJzaW9uGAMgAygDEhYKDnVwZGF0ZV92ZXJzaW9uGAQgAygDEhYKDmluc2VydF92ZXJzaW9uGAUgAygDEh4KFmluc2VydF9hdXRvX2lkX3ZlcnNpb24YBiADKAMSFgoOZGVsZXRlX3ZlcnNpb24YByADKAMitQEKC1JlYWRPcHRpb25zElcKEHJlYWRfY29uc2lzdGVuY3kYASABKA4yNC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SZWFkT3B0aW9ucy5SZWFkQ29uc2lzdGVuY3k6B0RFRkFVTFQSEwoLdHJhbnNhY3Rpb24YAiABKAwiOAoPUmVhZENvbnNpc3RlbmN5EgsKB0RFRkFVTFQQABIKCgZTVFJPTkcQARIMCghFVkVOVFVBTBACInYKDUxvb2t1cFJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSKQoDa2V5GAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5Iq4BCg5Mb29rdXBSZXNwb25zZRI0CgVmb3VuZBgBIAMoCzIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdBI2CgdtaXNzaW5nGAIgAygLMiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0Ei4KCGRlZmVycmVkGAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5IqsCCg9SdW5RdWVyeVJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSOgoMcGFydGl0aW9uX2lkGAIgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUGFydGl0aW9uSWQSLQoFcXVlcnkYAyABKAsyHi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeRI0CglncWxfcXVlcnkYByABKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeRIdChVtaW5fc2FmZV90aW1lX3NlY29uZHMYBCABKAMSHAoUc3VnZ2VzdGVkX2JhdGNoX3NpemUYBSABKAUiYgoQUnVuUXVlcnlSZXNwb25zZRI4CgViYXRjaBgBIAIoCzIpLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5UmVzdWx0QmF0Y2gSFAoMcXVlcnlfaGFuZGxlGAIgASgMIiwKFENvbnRpbnVlUXVlcnlSZXF1ZXN0EhQKDHF1ZXJ5X2hhbmRsZRgBIAIoDCJRChVDb250aW51ZVF1ZXJ5UmVzcG9uc2USOAoFYmF0Y2gYASACKAsyKS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeVJlc3VsdEJhdGNoIlMKF0JlZ2luVHJhbnNhY3Rpb25SZXF1ZXN0EhoKC2Nyb3NzX2dyb3VwGAEgASgIOgVmYWxzZRIcCg1jcm9zc19yZXF1ZXN0GAIgASgIOgVmYWxzZSIvChhCZWdpblRyYW5zYWN0aW9uUmVzcG9uc2USEwoLdHJhbnNhY3Rpb24YASACKAwiJgoPUm9sbGJhY2tSZXF1ZXN0EhMKC3RyYW5zYWN0aW9uGAEgAigMIhIKEFJvbGxiYWNrUmVzcG9uc2UiwAIKDUNvbW1pdFJlcXVlc3QSEwoLdHJhbnNhY3Rpb24YASABKAwSMwoIbXV0YXRpb24YBSADKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5NdXRhdGlvbhJIChNkZXByZWNhdGVkX211dGF0aW9uGAIgASgLMisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRGVwcmVjYXRlZE11dGF0aW9uEkgKBG1vZGUYBCABKA4yKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21taXRSZXF1ZXN0Lk1vZGU6DVRSQU5TQUNUSU9OQUwSHwoQaWdub3JlX3JlYWRfb25seRgGIAEoCDoFZmFsc2UiMAoETW9kZRIRCg1UUkFOU0FDVElPTkFMEAESFQoRTk9OX1RSQU5TQUNUSU9OQUwQAiLAAQoOQ29tbWl0UmVzcG9uc2USQAoPbXV0YXRpb25fcmVzdWx0GAMgAygLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb25SZXN1bHQSVQoaZGVwcmVjYXRlZF9tdXRhdGlvbl9yZXN1bHQYASABKAsyMS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5EZXByZWNhdGVkTXV0YXRpb25SZXN1bHQSFQoNaW5kZXhfdXBkYXRlcxgEIAEoBSJzChJBbGxvY2F0ZUlkc1JlcXVlc3QSLgoIYWxsb2NhdGUYASADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSLQoHcmVzZXJ2ZRgCIAMoCzIcLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktleSJGChNBbGxvY2F0ZUlkc1Jlc3BvbnNlEi8KCWFsbG9jYXRlZBgBIAMoCzIcLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktleSJYCgxXcml0ZVJlcXVlc3QSSAoTZGVwcmVjYXRlZF9tdXRhdGlvbhgBIAIoCzIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkRlcHJlY2F0ZWRNdXRhdGlvbjKiBwoSRGF0YXN0b3JlVjRTZXJ2aWNlEnkKEEJlZ2luVHJhbnNhY3Rpb24SMC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5CZWdpblRyYW5zYWN0aW9uUmVxdWVzdBoxLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJlZ2luVHJhbnNhY3Rpb25SZXNwb25zZSIAEmEKCFJvbGxiYWNrEiguYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUm9sbGJhY2tSZXF1ZXN0GikuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUm9sbGJhY2tSZXNwb25zZSIAElsKBkNvbW1pdBImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbW1pdFJlcXVlc3QaJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21taXRSZXNwb25zZSIAEmEKCFJ1blF1ZXJ5EiguYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUnVuUXVlcnlSZXF1ZXN0GikuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUnVuUXVlcnlSZXNwb25zZSIAEnAKDUNvbnRpbnVlUXVlcnkSLS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db250aW51ZVF1ZXJ5UmVxdWVzdBouLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbnRpbnVlUXVlcnlSZXNwb25zZSIAElsKBkxvb2t1cBImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkxvb2t1cFJlcXVlc3QaJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXNwb25zZSIAEmoKC0FsbG9jYXRlSWRzEisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQWxsb2NhdGVJZHNSZXF1ZXN0GiwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQWxsb2NhdGVJZHNSZXNwb25zZSIAElgKA0dldBImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkxvb2t1cFJlcXVlc3QaJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXNwb25zZSIAElkKBVdyaXRlEiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuV3JpdGVSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVzcG9uc2UiAEIjCh9jb20uZ29vZ2xlLmFwcGhvc3RpbmcuZGF0YXN0b3JlIAE=")) if _net_proto___parse__python is not None: _net_proto___parse__python.RegisterType( _SERIALIZED_DESCRIPTOR.tostring()) @@ -1497,6 +1497,10 @@ class Filter(ProtocolBuffer.ProtocolMessage): composite_filter_ = None has_property_filter_ = 0 property_filter_ = None + has_bounding_circle_filter_ = 0 + bounding_circle_filter_ = None + has_bounding_box_filter_ = 0 + bounding_box_filter_ = None def __init__(self, contents=None): self.lazy_init_lock_ = thread.allocate_lock() @@ -1540,11 +1544,51 @@ class Filter(ProtocolBuffer.ProtocolMessage): def has_property_filter(self): return self.has_property_filter_ + def bounding_circle_filter(self): + if self.bounding_circle_filter_ is None: + self.lazy_init_lock_.acquire() + try: + if self.bounding_circle_filter_ is None: self.bounding_circle_filter_ = BoundingCircleFilter() + finally: + self.lazy_init_lock_.release() + return self.bounding_circle_filter_ + + def mutable_bounding_circle_filter(self): self.has_bounding_circle_filter_ = 1; return self.bounding_circle_filter() + + def clear_bounding_circle_filter(self): + + if self.has_bounding_circle_filter_: + self.has_bounding_circle_filter_ = 0; + if self.bounding_circle_filter_ is not None: self.bounding_circle_filter_.Clear() + + def has_bounding_circle_filter(self): return self.has_bounding_circle_filter_ + + def bounding_box_filter(self): + if self.bounding_box_filter_ is None: + self.lazy_init_lock_.acquire() + try: + if self.bounding_box_filter_ is None: self.bounding_box_filter_ = BoundingBoxFilter() + finally: + self.lazy_init_lock_.release() + return self.bounding_box_filter_ + + def mutable_bounding_box_filter(self): self.has_bounding_box_filter_ = 1; return self.bounding_box_filter() + + def clear_bounding_box_filter(self): + + if self.has_bounding_box_filter_: + self.has_bounding_box_filter_ = 0; + if self.bounding_box_filter_ is not None: self.bounding_box_filter_.Clear() + + def has_bounding_box_filter(self): return self.has_bounding_box_filter_ + def MergeFrom(self, x): assert x is not self if (x.has_composite_filter()): self.mutable_composite_filter().MergeFrom(x.composite_filter()) if (x.has_property_filter()): self.mutable_property_filter().MergeFrom(x.property_filter()) + if (x.has_bounding_circle_filter()): self.mutable_bounding_circle_filter().MergeFrom(x.bounding_circle_filter()) + if (x.has_bounding_box_filter()): self.mutable_bounding_box_filter().MergeFrom(x.bounding_box_filter()) if _net_proto___parse__python is not None: def _CMergeFromString(self, s): @@ -1579,29 +1623,41 @@ class Filter(ProtocolBuffer.ProtocolMessage): if self.has_composite_filter_ and self.composite_filter_ != x.composite_filter_: return 0 if self.has_property_filter_ != x.has_property_filter_: return 0 if self.has_property_filter_ and self.property_filter_ != x.property_filter_: return 0 + if self.has_bounding_circle_filter_ != x.has_bounding_circle_filter_: return 0 + if self.has_bounding_circle_filter_ and self.bounding_circle_filter_ != x.bounding_circle_filter_: return 0 + if self.has_bounding_box_filter_ != x.has_bounding_box_filter_: return 0 + if self.has_bounding_box_filter_ and self.bounding_box_filter_ != x.bounding_box_filter_: return 0 return 1 def IsInitialized(self, debug_strs=None): initialized = 1 if (self.has_composite_filter_ and not self.composite_filter_.IsInitialized(debug_strs)): initialized = 0 if (self.has_property_filter_ and not self.property_filter_.IsInitialized(debug_strs)): initialized = 0 + if (self.has_bounding_circle_filter_ and not self.bounding_circle_filter_.IsInitialized(debug_strs)): initialized = 0 + if (self.has_bounding_box_filter_ and not self.bounding_box_filter_.IsInitialized(debug_strs)): initialized = 0 return initialized def ByteSize(self): n = 0 if (self.has_composite_filter_): n += 1 + self.lengthString(self.composite_filter_.ByteSize()) if (self.has_property_filter_): n += 1 + self.lengthString(self.property_filter_.ByteSize()) + if (self.has_bounding_circle_filter_): n += 1 + self.lengthString(self.bounding_circle_filter_.ByteSize()) + if (self.has_bounding_box_filter_): n += 1 + self.lengthString(self.bounding_box_filter_.ByteSize()) return n def ByteSizePartial(self): n = 0 if (self.has_composite_filter_): n += 1 + self.lengthString(self.composite_filter_.ByteSizePartial()) if (self.has_property_filter_): n += 1 + self.lengthString(self.property_filter_.ByteSizePartial()) + if (self.has_bounding_circle_filter_): n += 1 + self.lengthString(self.bounding_circle_filter_.ByteSizePartial()) + if (self.has_bounding_box_filter_): n += 1 + self.lengthString(self.bounding_box_filter_.ByteSizePartial()) return n def Clear(self): self.clear_composite_filter() self.clear_property_filter() + self.clear_bounding_circle_filter() + self.clear_bounding_box_filter() def OutputUnchecked(self, out): if (self.has_composite_filter_): @@ -1612,6 +1668,14 @@ class Filter(ProtocolBuffer.ProtocolMessage): out.putVarInt32(18) out.putVarInt32(self.property_filter_.ByteSize()) self.property_filter_.OutputUnchecked(out) + if (self.has_bounding_circle_filter_): + out.putVarInt32(26) + out.putVarInt32(self.bounding_circle_filter_.ByteSize()) + self.bounding_circle_filter_.OutputUnchecked(out) + if (self.has_bounding_box_filter_): + out.putVarInt32(34) + out.putVarInt32(self.bounding_box_filter_.ByteSize()) + self.bounding_box_filter_.OutputUnchecked(out) def OutputPartial(self, out): if (self.has_composite_filter_): @@ -1622,6 +1686,14 @@ class Filter(ProtocolBuffer.ProtocolMessage): out.putVarInt32(18) out.putVarInt32(self.property_filter_.ByteSizePartial()) self.property_filter_.OutputPartial(out) + if (self.has_bounding_circle_filter_): + out.putVarInt32(26) + out.putVarInt32(self.bounding_circle_filter_.ByteSizePartial()) + self.bounding_circle_filter_.OutputPartial(out) + if (self.has_bounding_box_filter_): + out.putVarInt32(34) + out.putVarInt32(self.bounding_box_filter_.ByteSizePartial()) + self.bounding_box_filter_.OutputPartial(out) def TryMerge(self, d): while d.avail() > 0: @@ -1638,6 +1710,18 @@ class Filter(ProtocolBuffer.ProtocolMessage): d.skip(length) self.mutable_property_filter().TryMerge(tmp) continue + if tt == 26: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_bounding_circle_filter().TryMerge(tmp) + continue + if tt == 34: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_bounding_box_filter().TryMerge(tmp) + continue if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError @@ -1654,6 +1738,14 @@ class Filter(ProtocolBuffer.ProtocolMessage): res+=prefix+"property_filter <\n" res+=self.property_filter_.__str__(prefix + " ", printElemNumber) res+=prefix+">\n" + if self.has_bounding_circle_filter_: + res+=prefix+"bounding_circle_filter <\n" + res+=self.bounding_circle_filter_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + if self.has_bounding_box_filter_: + res+=prefix+"bounding_box_filter <\n" + res+=self.bounding_box_filter_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" return res @@ -1662,25 +1754,31 @@ class Filter(ProtocolBuffer.ProtocolMessage): kcomposite_filter = 1 kproperty_filter = 2 + kbounding_circle_filter = 3 + kbounding_box_filter = 4 _TEXT = _BuildTagLookupTable({ 0: "ErrorCode", 1: "composite_filter", 2: "property_filter", - }, 2) + 3: "bounding_circle_filter", + 4: "bounding_box_filter", + }, 4) _TYPES = _BuildTagLookupTable({ 0: ProtocolBuffer.Encoder.NUMERIC, 1: ProtocolBuffer.Encoder.STRING, 2: ProtocolBuffer.Encoder.STRING, - }, 2, ProtocolBuffer.Encoder.MAX_TYPE) + 3: ProtocolBuffer.Encoder.STRING, + 4: ProtocolBuffer.Encoder.STRING, + }, 4, ProtocolBuffer.Encoder.MAX_TYPE) _STYLE = """""" _STYLE_CONTENT_TYPE = """""" _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.Filter' _SERIALIZED_DESCRIPTOR = array.array('B') - _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlchMaEGNvbXBvc2l0ZV9maWx0ZXIgASgCMAs4AUonYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tcG9zaXRlRmlsdGVyowGqAQVjdHlwZbIBBnByb3RvMqQBFBMaD3Byb3BlcnR5X2ZpbHRlciACKAIwCzgBSiZhcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUZpbHRlcqMBqgEFY3R5cGWyAQZwcm90bzKkARTCAR1hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FcnJvcg==")) + _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlchMaEGNvbXBvc2l0ZV9maWx0ZXIgASgCMAs4AUonYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tcG9zaXRlRmlsdGVyowGqAQVjdHlwZbIBBnByb3RvMqQBFBMaD3Byb3BlcnR5X2ZpbHRlciACKAIwCzgBSiZhcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUZpbHRlcqMBqgEFY3R5cGWyAQZwcm90bzKkARQTGhZib3VuZGluZ19jaXJjbGVfZmlsdGVyIAMoAjALOAFKLGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJvdW5kaW5nQ2lyY2xlRmlsdGVyowGqAQVjdHlwZbIBBnByb3RvMqQBFBMaE2JvdW5kaW5nX2JveF9maWx0ZXIgBCgCMAs4AUopYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQm91bmRpbmdCb3hGaWx0ZXKjAaoBBWN0eXBlsgEGcHJvdG8ypAEUwgEdYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRXJyb3I=")) if _net_proto___parse__python is not None: _net_proto___parse__python.RegisterType( _SERIALIZED_DESCRIPTOR.tostring()) @@ -2122,6 +2220,446 @@ class PropertyFilter(ProtocolBuffer.ProtocolMessage): _net_proto___parse__python.RegisterType( _SERIALIZED_DESCRIPTOR.tostring()) +class BoundingCircleFilter(ProtocolBuffer.ProtocolMessage): + has_property_ = 0 + has_center_ = 0 + has_radius_meters_ = 0 + radius_meters_ = 0.0 + + def __init__(self, contents=None): + self.property_ = PropertyReference() + self.center_ = google.appengine.datastore.entity_v4_pb.GeoPoint() + if contents is not None: self.MergeFromString(contents) + + def property(self): return self.property_ + + def mutable_property(self): self.has_property_ = 1; return self.property_ + + def clear_property(self):self.has_property_ = 0; self.property_.Clear() + + def has_property(self): return self.has_property_ + + def center(self): return self.center_ + + def mutable_center(self): self.has_center_ = 1; return self.center_ + + def clear_center(self):self.has_center_ = 0; self.center_.Clear() + + def has_center(self): return self.has_center_ + + def radius_meters(self): return self.radius_meters_ + + def set_radius_meters(self, x): + self.has_radius_meters_ = 1 + self.radius_meters_ = x + + def clear_radius_meters(self): + if self.has_radius_meters_: + self.has_radius_meters_ = 0 + self.radius_meters_ = 0.0 + + def has_radius_meters(self): return self.has_radius_meters_ + + + def MergeFrom(self, x): + assert x is not self + if (x.has_property()): self.mutable_property().MergeFrom(x.property()) + if (x.has_center()): self.mutable_center().MergeFrom(x.center()) + if (x.has_radius_meters()): self.set_radius_meters(x.radius_meters()) + + if _net_proto___parse__python is not None: + def _CMergeFromString(self, s): + _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.BoundingCircleFilter', s) + + if _net_proto___parse__python is not None: + def _CEncode(self): + return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.BoundingCircleFilter') + + if _net_proto___parse__python is not None: + def _CEncodePartial(self): + return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.BoundingCircleFilter') + + if _net_proto___parse__python is not None: + def _CToASCII(self, output_format): + return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.BoundingCircleFilter', output_format) + + + if _net_proto___parse__python is not None: + def ParseASCII(self, s): + _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.BoundingCircleFilter', s) + + + if _net_proto___parse__python is not None: + def ParseASCIIIgnoreUnknown(self, s): + _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.BoundingCircleFilter', s) + + + def Equals(self, x): + if x is self: return 1 + if self.has_property_ != x.has_property_: return 0 + if self.has_property_ and self.property_ != x.property_: return 0 + if self.has_center_ != x.has_center_: return 0 + if self.has_center_ and self.center_ != x.center_: return 0 + if self.has_radius_meters_ != x.has_radius_meters_: return 0 + if self.has_radius_meters_ and self.radius_meters_ != x.radius_meters_: return 0 + return 1 + + def IsInitialized(self, debug_strs=None): + initialized = 1 + if (not self.has_property_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: property not set.') + elif not self.property_.IsInitialized(debug_strs): initialized = 0 + if (not self.has_center_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: center not set.') + elif not self.center_.IsInitialized(debug_strs): initialized = 0 + if (not self.has_radius_meters_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: radius_meters not set.') + return initialized + + def ByteSize(self): + n = 0 + n += self.lengthString(self.property_.ByteSize()) + n += self.lengthString(self.center_.ByteSize()) + return n + 11 + + def ByteSizePartial(self): + n = 0 + if (self.has_property_): + n += 1 + n += self.lengthString(self.property_.ByteSizePartial()) + if (self.has_center_): + n += 1 + n += self.lengthString(self.center_.ByteSizePartial()) + if (self.has_radius_meters_): + n += 9 + return n + + def Clear(self): + self.clear_property() + self.clear_center() + self.clear_radius_meters() + + def OutputUnchecked(self, out): + out.putVarInt32(10) + out.putVarInt32(self.property_.ByteSize()) + self.property_.OutputUnchecked(out) + out.putVarInt32(18) + out.putVarInt32(self.center_.ByteSize()) + self.center_.OutputUnchecked(out) + out.putVarInt32(25) + out.putDouble(self.radius_meters_) + + def OutputPartial(self, out): + if (self.has_property_): + out.putVarInt32(10) + out.putVarInt32(self.property_.ByteSizePartial()) + self.property_.OutputPartial(out) + if (self.has_center_): + out.putVarInt32(18) + out.putVarInt32(self.center_.ByteSizePartial()) + self.center_.OutputPartial(out) + if (self.has_radius_meters_): + out.putVarInt32(25) + out.putDouble(self.radius_meters_) + + def TryMerge(self, d): + while d.avail() > 0: + tt = d.getVarInt32() + if tt == 10: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_property().TryMerge(tmp) + continue + if tt == 18: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_center().TryMerge(tmp) + continue + if tt == 25: + self.set_radius_meters(d.getDouble()) + continue + + + if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError + d.skipData(tt) + + + def __str__(self, prefix="", printElemNumber=0): + res="" + if self.has_property_: + res+=prefix+"property <\n" + res+=self.property_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + if self.has_center_: + res+=prefix+"center <\n" + res+=self.center_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + if self.has_radius_meters_: res+=prefix+("radius_meters: %s\n" % self.DebugFormat(self.radius_meters_)) + return res + + + def _BuildTagLookupTable(sparse, maxtag, default=None): + return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)]) + + kproperty = 1 + kcenter = 2 + kradius_meters = 3 + + _TEXT = _BuildTagLookupTable({ + 0: "ErrorCode", + 1: "property", + 2: "center", + 3: "radius_meters", + }, 3) + + _TYPES = _BuildTagLookupTable({ + 0: ProtocolBuffer.Encoder.NUMERIC, + 1: ProtocolBuffer.Encoder.STRING, + 2: ProtocolBuffer.Encoder.STRING, + 3: ProtocolBuffer.Encoder.DOUBLE, + }, 3, ProtocolBuffer.Encoder.MAX_TYPE) + + + _STYLE = """""" + _STYLE_CONTENT_TYPE = """""" + _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.BoundingCircleFilter' + _SERIALIZED_DESCRIPTOR = array.array('B') + _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KLGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJvdW5kaW5nQ2lyY2xlRmlsdGVyExoIcHJvcGVydHkgASgCMAs4AkopYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlSZWZlcmVuY2WjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoGY2VudGVyIAIoAjALOAJKIGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkdlb1BvaW50owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaDXJhZGl1c19tZXRlcnMgAygBMAE4AhTCAR1hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FcnJvcg==")) + if _net_proto___parse__python is not None: + _net_proto___parse__python.RegisterType( + _SERIALIZED_DESCRIPTOR.tostring()) + +class BoundingBoxFilter(ProtocolBuffer.ProtocolMessage): + has_property_ = 0 + has_southwest_ = 0 + has_northeast_ = 0 + + def __init__(self, contents=None): + self.property_ = PropertyReference() + self.southwest_ = google.appengine.datastore.entity_v4_pb.GeoPoint() + self.northeast_ = google.appengine.datastore.entity_v4_pb.GeoPoint() + if contents is not None: self.MergeFromString(contents) + + def property(self): return self.property_ + + def mutable_property(self): self.has_property_ = 1; return self.property_ + + def clear_property(self):self.has_property_ = 0; self.property_.Clear() + + def has_property(self): return self.has_property_ + + def southwest(self): return self.southwest_ + + def mutable_southwest(self): self.has_southwest_ = 1; return self.southwest_ + + def clear_southwest(self):self.has_southwest_ = 0; self.southwest_.Clear() + + def has_southwest(self): return self.has_southwest_ + + def northeast(self): return self.northeast_ + + def mutable_northeast(self): self.has_northeast_ = 1; return self.northeast_ + + def clear_northeast(self):self.has_northeast_ = 0; self.northeast_.Clear() + + def has_northeast(self): return self.has_northeast_ + + + def MergeFrom(self, x): + assert x is not self + if (x.has_property()): self.mutable_property().MergeFrom(x.property()) + if (x.has_southwest()): self.mutable_southwest().MergeFrom(x.southwest()) + if (x.has_northeast()): self.mutable_northeast().MergeFrom(x.northeast()) + + if _net_proto___parse__python is not None: + def _CMergeFromString(self, s): + _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.BoundingBoxFilter', s) + + if _net_proto___parse__python is not None: + def _CEncode(self): + return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.BoundingBoxFilter') + + if _net_proto___parse__python is not None: + def _CEncodePartial(self): + return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.BoundingBoxFilter') + + if _net_proto___parse__python is not None: + def _CToASCII(self, output_format): + return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.BoundingBoxFilter', output_format) + + + if _net_proto___parse__python is not None: + def ParseASCII(self, s): + _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.BoundingBoxFilter', s) + + + if _net_proto___parse__python is not None: + def ParseASCIIIgnoreUnknown(self, s): + _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.BoundingBoxFilter', s) + + + def Equals(self, x): + if x is self: return 1 + if self.has_property_ != x.has_property_: return 0 + if self.has_property_ and self.property_ != x.property_: return 0 + if self.has_southwest_ != x.has_southwest_: return 0 + if self.has_southwest_ and self.southwest_ != x.southwest_: return 0 + if self.has_northeast_ != x.has_northeast_: return 0 + if self.has_northeast_ and self.northeast_ != x.northeast_: return 0 + return 1 + + def IsInitialized(self, debug_strs=None): + initialized = 1 + if (not self.has_property_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: property not set.') + elif not self.property_.IsInitialized(debug_strs): initialized = 0 + if (not self.has_southwest_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: southwest not set.') + elif not self.southwest_.IsInitialized(debug_strs): initialized = 0 + if (not self.has_northeast_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: northeast not set.') + elif not self.northeast_.IsInitialized(debug_strs): initialized = 0 + return initialized + + def ByteSize(self): + n = 0 + n += self.lengthString(self.property_.ByteSize()) + n += self.lengthString(self.southwest_.ByteSize()) + n += self.lengthString(self.northeast_.ByteSize()) + return n + 3 + + def ByteSizePartial(self): + n = 0 + if (self.has_property_): + n += 1 + n += self.lengthString(self.property_.ByteSizePartial()) + if (self.has_southwest_): + n += 1 + n += self.lengthString(self.southwest_.ByteSizePartial()) + if (self.has_northeast_): + n += 1 + n += self.lengthString(self.northeast_.ByteSizePartial()) + return n + + def Clear(self): + self.clear_property() + self.clear_southwest() + self.clear_northeast() + + def OutputUnchecked(self, out): + out.putVarInt32(10) + out.putVarInt32(self.property_.ByteSize()) + self.property_.OutputUnchecked(out) + out.putVarInt32(18) + out.putVarInt32(self.southwest_.ByteSize()) + self.southwest_.OutputUnchecked(out) + out.putVarInt32(26) + out.putVarInt32(self.northeast_.ByteSize()) + self.northeast_.OutputUnchecked(out) + + def OutputPartial(self, out): + if (self.has_property_): + out.putVarInt32(10) + out.putVarInt32(self.property_.ByteSizePartial()) + self.property_.OutputPartial(out) + if (self.has_southwest_): + out.putVarInt32(18) + out.putVarInt32(self.southwest_.ByteSizePartial()) + self.southwest_.OutputPartial(out) + if (self.has_northeast_): + out.putVarInt32(26) + out.putVarInt32(self.northeast_.ByteSizePartial()) + self.northeast_.OutputPartial(out) + + def TryMerge(self, d): + while d.avail() > 0: + tt = d.getVarInt32() + if tt == 10: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_property().TryMerge(tmp) + continue + if tt == 18: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_southwest().TryMerge(tmp) + continue + if tt == 26: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_northeast().TryMerge(tmp) + continue + + + if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError + d.skipData(tt) + + + def __str__(self, prefix="", printElemNumber=0): + res="" + if self.has_property_: + res+=prefix+"property <\n" + res+=self.property_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + if self.has_southwest_: + res+=prefix+"southwest <\n" + res+=self.southwest_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + if self.has_northeast_: + res+=prefix+"northeast <\n" + res+=self.northeast_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + return res + + + def _BuildTagLookupTable(sparse, maxtag, default=None): + return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)]) + + kproperty = 1 + ksouthwest = 2 + knortheast = 3 + + _TEXT = _BuildTagLookupTable({ + 0: "ErrorCode", + 1: "property", + 2: "southwest", + 3: "northeast", + }, 3) + + _TYPES = _BuildTagLookupTable({ + 0: ProtocolBuffer.Encoder.NUMERIC, + 1: ProtocolBuffer.Encoder.STRING, + 2: ProtocolBuffer.Encoder.STRING, + 3: ProtocolBuffer.Encoder.STRING, + }, 3, ProtocolBuffer.Encoder.MAX_TYPE) + + + _STYLE = """""" + _STYLE_CONTENT_TYPE = """""" + _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.BoundingBoxFilter' + _SERIALIZED_DESCRIPTOR = array.array('B') + _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KKWFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkJvdW5kaW5nQm94RmlsdGVyExoIcHJvcGVydHkgASgCMAs4AkopYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlSZWZlcmVuY2WjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoJc291dGh3ZXN0IAIoAjALOAJKIGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lkdlb1BvaW50owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaCW5vcnRoZWFzdCADKAIwCzgCSiBhcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HZW9Qb2ludKMBqgEFY3R5cGWyAQZwcm90bzKkARTCAR1hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FcnJvcg==")) + if _net_proto___parse__python is not None: + _net_proto___parse__python.RegisterType( + _SERIALIZED_DESCRIPTOR.tostring()) + class GqlQuery(ProtocolBuffer.ProtocolMessage): has_query_string_ = 0 query_string_ = "" @@ -7676,4 +8214,4 @@ class DatastoreV4Service(_server_stub_base_class): if _extension_runtime: pass -__all__ = ['Error','EntityResult','Query','KindExpression','PropertyReference','PropertyExpression','PropertyOrder','Filter','CompositeFilter','PropertyFilter','GqlQuery','GqlQueryArg','QueryResultBatch','Mutation','MutationResult','DeprecatedMutation','DeprecatedMutationResult','ReadOptions','LookupRequest','LookupResponse','RunQueryRequest','RunQueryResponse','ContinueQueryRequest','ContinueQueryResponse','BeginTransactionRequest','BeginTransactionResponse','RollbackRequest','RollbackResponse','CommitRequest','CommitResponse','AllocateIdsRequest','AllocateIdsResponse','WriteRequest','DatastoreV4Service'] +__all__ = ['Error','EntityResult','Query','KindExpression','PropertyReference','PropertyExpression','PropertyOrder','Filter','CompositeFilter','PropertyFilter','BoundingCircleFilter','BoundingBoxFilter','GqlQuery','GqlQueryArg','QueryResultBatch','Mutation','MutationResult','DeprecatedMutation','DeprecatedMutationResult','ReadOptions','LookupRequest','LookupResponse','RunQueryRequest','RunQueryResponse','ContinueQueryRequest','ContinueQueryResponse','BeginTransactionRequest','BeginTransactionResponse','RollbackRequest','RollbackResponse','CommitRequest','CommitResponse','AllocateIdsRequest','AllocateIdsResponse','WriteRequest','DatastoreV4Service'] diff --git a/python/google/appengine/datastore/datastore_v4_stub.py b/python/google/appengine/datastore/datastore_v4_stub.py index 2d27f025..7297d292 100644 --- a/python/google/appengine/datastore/datastore_v4_stub.py +++ b/python/google/appengine/datastore/datastore_v4_stub.py @@ -38,6 +38,7 @@ from google.appengine.api import apiproxy_stub from google.appengine.api import apiproxy_stub_map from google.appengine.datastore import datastore_pb from google.appengine.datastore import datastore_pbs +from google.appengine.datastore import datastore_query from google.appengine.datastore import datastore_stub_util from google.appengine.datastore import datastore_v4_pb from google.appengine.datastore import datastore_v4_validator @@ -121,13 +122,118 @@ class DatastoreV4Stub(apiproxy_stub.APIProxyStub): raise apiproxy_errors.ApplicationError( datastore_v4_pb.Error.BAD_REQUEST, str(e)) + def _GetQueryCompositeFilter(self, filters, operator): + """Wraps the filters in a datastore_query.CompositeFilter if length > 1.""" + + if not filters: + return None + elif len(filters) == 1: + return filters[0] + else: + return datastore_query.CompositeFilter(operator, filters) + + def _GetV4PbCompositeFilter(self, filter_pbs, operator_pb): + """Wraps the filters in a datastore_v4_pb.CompositeFilter if length > 1.""" + if not filter_pbs: + return None + elif len(filter_pbs) == 1: + return filter_pbs[0] + else: + res_filter_pb = datastore_v4_pb.Filter() + composite_filter_pb = res_filter_pb.mutable_composite_filter() + composite_filter_pb.set_operator(operator_pb) + composite_filter_pb.filter_list().extend(filter_pbs) + return res_filter_pb + + def _GetFilterPbList(self, filter_pb): + if filter_pb.has_composite_filter(): + composite_filter = filter_pb.composite_filter() + assert composite_filter.operator() == datastore_v4_pb.CompositeFilter.AND + + return composite_filter.filter_list() + else: + return [filter_pb] + + def _ConvertGeospatialFilterOrNone(self, filter_pb): + """Converts geo-spatial filters to filter predicates.""" + + if filter_pb.has_bounding_circle_filter(): + return (datastore_query._BoundingCircleFilter._from_v4_pb( + filter_pb.bounding_circle_filter())) + else: + return None + + def _SplitGeospatialFilters(self, req): + """Extracts, converts and removes geo-filters from a request. + + Args: + req: a datastore_v4_pb.RunQueryRequest + + Returns: + a pair (new_req, filter_predicate) where new_req is req with unsupported + filters removed and filter_predicate is a datastore_query.FilterPredicate + with the unsupported filters. filter_predicate is None if no unsupported + filters were removed. + """ + + assert datastore_v4_pb.CompositeFilter._Operator_NAMES.values() == ['AND'] + + + + filter_predicate = None + new_req = datastore_v4_pb.RunQueryRequest() + new_req.CopyFrom(req) + + query = new_req.mutable_query() + + + sub_filter_pbs = [] + sub_filter_predicates = [] + + for filter_pb in self._GetFilterPbList(req.query().filter()): + sub_filter_predicate = self._ConvertGeospatialFilterOrNone(filter_pb) + + if sub_filter_predicate is None: + sub_filter_pbs.append(filter_pb) + else: + sub_filter_predicates.append(sub_filter_predicate) + + op_pb = datastore_v4_pb.CompositeFilter.AND + op = datastore_query.CompositeFilter.AND + + filter_pb = self._GetV4PbCompositeFilter(sub_filter_pbs, op_pb) + filter_predicate = self._GetQueryCompositeFilter(sub_filter_predicates, op) + + + + if filter_pb is None: + query.clear_filter() + else: + query.mutable_filter().CopyFrom(filter_pb) + + return (new_req, filter_predicate) + def _Dynamic_RunQuery(self, req, resp): try: self.__normalize_v4_run_query_request(req) self.__service_validator.validate_run_query_req(req) - v3_req = self.__service_converter.v4_run_query_req_to_v3_query(req) - v3_resp = datastore_pb.QueryResult() - self.__make_v3_call('RunQuery', v3_req, v3_resp) + + v3_stub = apiproxy_stub_map.apiproxy.GetStub(V3_SERVICE_NAME) + + new_req, filter_predicate = self._SplitGeospatialFilters(req) + + + + + if (issubclass(v3_stub.__class__, datastore_stub_util.BaseDatastore) + and filter_predicate is not None): + v3_req = self.__service_converter.v4_run_query_req_to_v3_query(new_req) + v3_resp = datastore_pb.QueryResult() + v3_stub._Dynamic_RunQuery(v3_req, v3_resp, filter_predicate) + else: + v3_req = self.__service_converter.v4_run_query_req_to_v3_query(req) + v3_resp = datastore_pb.QueryResult() + self.__make_v3_call('RunQuery', v3_req, v3_resp) except datastore_pbs.InvalidConversionError, e: raise apiproxy_errors.ApplicationError( datastore_v4_pb.Error.BAD_REQUEST, str(e)) diff --git a/python/google/appengine/datastore/datastore_v4_validator.py b/python/google/appengine/datastore/datastore_v4_validator.py index 94069b5f..403efeae 100644 --- a/python/google/appengine/datastore/datastore_v4_validator.py +++ b/python/google/appengine/datastore/datastore_v4_validator.py @@ -882,7 +882,9 @@ class _QueryValidator(object): ValidationError: if the filter is invalid """ _assert_condition((filt.has_composite_filter() - + filt.has_property_filter() == 1), + + filt.has_property_filter() + + filt.has_bounding_circle_filter() + + filt.has_bounding_box_filter() == 1), 'A filter must have exactly one of its fields set.') if filt.has_composite_filter(): comp_filter = filt.composite_filter() diff --git a/python/google/appengine/datastore/entity_pb.py b/python/google/appengine/datastore/entity_pb.py index b42c5eef..2e3d9086 100644 --- a/python/google/appengine/datastore/entity_pb.py +++ b/python/google/appengine/datastore/entity_pb.py @@ -2836,10 +2836,12 @@ class CompositeProperty(ProtocolBuffer.ProtocolMessage): class Index_Property(ProtocolBuffer.ProtocolMessage): + DIRECTION_UNSPECIFIED = 0 ASCENDING = 1 DESCENDING = 2 _Direction_NAMES = { + 0: "DIRECTION_UNSPECIFIED", 1: "ASCENDING", 2: "DESCENDING", } @@ -2847,10 +2849,25 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): def Direction_Name(cls, x): return cls._Direction_NAMES.get(x, "") Direction_Name = classmethod(Direction_Name) + + + MODE_UNSPECIFIED = 0 + GEOSPATIAL = 3 + + _Mode_NAMES = { + 0: "MODE_UNSPECIFIED", + 3: "GEOSPATIAL", + } + + def Mode_Name(cls, x): return cls._Mode_NAMES.get(x, "") + Mode_Name = classmethod(Mode_Name) + has_name_ = 0 name_ = "" has_direction_ = 0 direction_ = 1 + has_mode_ = 0 + mode_ = 0 def __init__(self, contents=None): if contents is not None: self.MergeFromString(contents) @@ -2881,11 +2898,25 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): def has_direction(self): return self.has_direction_ + def mode(self): return self.mode_ + + def set_mode(self, x): + self.has_mode_ = 1 + self.mode_ = x + + def clear_mode(self): + if self.has_mode_: + self.has_mode_ = 0 + self.mode_ = 0 + + def has_mode(self): return self.has_mode_ + def MergeFrom(self, x): assert x is not self if (x.has_name()): self.set_name(x.name()) if (x.has_direction()): self.set_direction(x.direction()) + if (x.has_mode()): self.set_mode(x.mode()) def Equals(self, x): if x is self: return 1 @@ -2893,6 +2924,8 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): if self.has_name_ and self.name_ != x.name_: return 0 if self.has_direction_ != x.has_direction_: return 0 if self.has_direction_ and self.direction_ != x.direction_: return 0 + if self.has_mode_ != x.has_mode_: return 0 + if self.has_mode_ and self.mode_ != x.mode_: return 0 return 1 def IsInitialized(self, debug_strs=None): @@ -2907,6 +2940,7 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): n = 0 n += self.lengthString(len(self.name_)) if (self.has_direction_): n += 1 + self.lengthVarInt64(self.direction_) + if (self.has_mode_): n += 1 + self.lengthVarInt64(self.mode_) return n + 1 def ByteSizePartial(self): @@ -2915,11 +2949,13 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): n += 1 n += self.lengthString(len(self.name_)) if (self.has_direction_): n += 1 + self.lengthVarInt64(self.direction_) + if (self.has_mode_): n += 1 + self.lengthVarInt64(self.mode_) return n def Clear(self): self.clear_name() self.clear_direction() + self.clear_mode() def OutputUnchecked(self, out): out.putVarInt32(26) @@ -2927,6 +2963,9 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): if (self.has_direction_): out.putVarInt32(32) out.putVarInt32(self.direction_) + if (self.has_mode_): + out.putVarInt32(48) + out.putVarInt32(self.mode_) def OutputPartial(self, out): if (self.has_name_): @@ -2935,6 +2974,9 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): if (self.has_direction_): out.putVarInt32(32) out.putVarInt32(self.direction_) + if (self.has_mode_): + out.putVarInt32(48) + out.putVarInt32(self.mode_) def TryMerge(self, d): while 1: @@ -2946,6 +2988,9 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): if tt == 32: self.set_direction(d.getVarInt32()) continue + if tt == 48: + self.set_mode(d.getVarInt32()) + continue if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError @@ -2956,6 +3001,7 @@ class Index_Property(ProtocolBuffer.ProtocolMessage): res="" if self.has_name_: res+=prefix+("name: %s\n" % self.DebugFormatString(self.name_)) if self.has_direction_: res+=prefix+("direction: %s\n" % self.DebugFormatInt32(self.direction_)) + if self.has_mode_: res+=prefix+("mode: %s\n" % self.DebugFormatInt32(self.mode_)) return res class Index(ProtocolBuffer.ProtocolMessage): @@ -3128,6 +3174,7 @@ class Index(ProtocolBuffer.ProtocolMessage): kPropertyGroup = 2 kPropertyname = 3 kPropertydirection = 4 + kPropertymode = 6 _TEXT = _BuildTagLookupTable({ 0: "ErrorCode", @@ -3136,7 +3183,8 @@ class Index(ProtocolBuffer.ProtocolMessage): 3: "name", 4: "direction", 5: "ancestor", - }, 5) + 6: "mode", + }, 6) _TYPES = _BuildTagLookupTable({ 0: ProtocolBuffer.Encoder.NUMERIC, @@ -3145,7 +3193,8 @@ class Index(ProtocolBuffer.ProtocolMessage): 3: ProtocolBuffer.Encoder.STRING, 4: ProtocolBuffer.Encoder.NUMERIC, 5: ProtocolBuffer.Encoder.NUMERIC, - }, 5, ProtocolBuffer.Encoder.MAX_TYPE) + 6: ProtocolBuffer.Encoder.NUMERIC, + }, 6, ProtocolBuffer.Encoder.MAX_TYPE) _STYLE = """""" @@ -3178,9 +3227,12 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): state_ = 0 has_only_use_if_required_ = 0 only_use_if_required_ = 0 + has_write_division_family_ = 0 + write_division_family_ = "" def __init__(self, contents=None): self.definition_ = Index() + self.read_division_family_ = [] if contents is not None: self.MergeFromString(contents) def app_id(self): return self.app_id_ @@ -3243,6 +3295,34 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): def has_only_use_if_required(self): return self.has_only_use_if_required_ + def read_division_family_size(self): return len(self.read_division_family_) + def read_division_family_list(self): return self.read_division_family_ + + def read_division_family(self, i): + return self.read_division_family_[i] + + def set_read_division_family(self, i, x): + self.read_division_family_[i] = x + + def add_read_division_family(self, x): + self.read_division_family_.append(x) + + def clear_read_division_family(self): + self.read_division_family_ = [] + + def write_division_family(self): return self.write_division_family_ + + def set_write_division_family(self, x): + self.has_write_division_family_ = 1 + self.write_division_family_ = x + + def clear_write_division_family(self): + if self.has_write_division_family_: + self.has_write_division_family_ = 0 + self.write_division_family_ = "" + + def has_write_division_family(self): return self.has_write_division_family_ + def MergeFrom(self, x): assert x is not self @@ -3251,6 +3331,8 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): if (x.has_definition()): self.mutable_definition().MergeFrom(x.definition()) if (x.has_state()): self.set_state(x.state()) if (x.has_only_use_if_required()): self.set_only_use_if_required(x.only_use_if_required()) + for i in xrange(x.read_division_family_size()): self.add_read_division_family(x.read_division_family(i)) + if (x.has_write_division_family()): self.set_write_division_family(x.write_division_family()) def Equals(self, x): if x is self: return 1 @@ -3264,6 +3346,11 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): if self.has_state_ and self.state_ != x.state_: return 0 if self.has_only_use_if_required_ != x.has_only_use_if_required_: return 0 if self.has_only_use_if_required_ and self.only_use_if_required_ != x.only_use_if_required_: return 0 + if len(self.read_division_family_) != len(x.read_division_family_): return 0 + for e1, e2 in zip(self.read_division_family_, x.read_division_family_): + if e1 != e2: return 0 + if self.has_write_division_family_ != x.has_write_division_family_: return 0 + if self.has_write_division_family_ and self.write_division_family_ != x.write_division_family_: return 0 return 1 def IsInitialized(self, debug_strs=None): @@ -3294,6 +3381,9 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): n += self.lengthString(self.definition_.ByteSize()) n += self.lengthVarInt64(self.state_) if (self.has_only_use_if_required_): n += 2 + n += 1 * len(self.read_division_family_) + for i in xrange(len(self.read_division_family_)): n += self.lengthString(len(self.read_division_family_[i])) + if (self.has_write_division_family_): n += 1 + self.lengthString(len(self.write_division_family_)) return n + 4 def ByteSizePartial(self): @@ -3311,6 +3401,9 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): n += 1 n += self.lengthVarInt64(self.state_) if (self.has_only_use_if_required_): n += 2 + n += 1 * len(self.read_division_family_) + for i in xrange(len(self.read_division_family_)): n += self.lengthString(len(self.read_division_family_[i])) + if (self.has_write_division_family_): n += 1 + self.lengthString(len(self.write_division_family_)) return n def Clear(self): @@ -3319,6 +3412,8 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): self.clear_definition() self.clear_state() self.clear_only_use_if_required() + self.clear_read_division_family() + self.clear_write_division_family() def OutputUnchecked(self, out): out.putVarInt32(10) @@ -3333,6 +3428,12 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): if (self.has_only_use_if_required_): out.putVarInt32(48) out.putBoolean(self.only_use_if_required_) + for i in xrange(len(self.read_division_family_)): + out.putVarInt32(58) + out.putPrefixedString(self.read_division_family_[i]) + if (self.has_write_division_family_): + out.putVarInt32(66) + out.putPrefixedString(self.write_division_family_) def OutputPartial(self, out): if (self.has_app_id_): @@ -3351,6 +3452,12 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): if (self.has_only_use_if_required_): out.putVarInt32(48) out.putBoolean(self.only_use_if_required_) + for i in xrange(len(self.read_division_family_)): + out.putVarInt32(58) + out.putPrefixedString(self.read_division_family_[i]) + if (self.has_write_division_family_): + out.putVarInt32(66) + out.putPrefixedString(self.write_division_family_) def TryMerge(self, d): while d.avail() > 0: @@ -3373,6 +3480,12 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): if tt == 48: self.set_only_use_if_required(d.getBoolean()) continue + if tt == 58: + self.add_read_division_family(d.getPrefixedString()) + continue + if tt == 66: + self.set_write_division_family(d.getPrefixedString()) + continue if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError @@ -3389,6 +3502,13 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): res+=prefix+">\n" if self.has_state_: res+=prefix+("state: %s\n" % self.DebugFormatInt32(self.state_)) if self.has_only_use_if_required_: res+=prefix+("only_use_if_required: %s\n" % self.DebugFormatBool(self.only_use_if_required_)) + cnt=0 + for e in self.read_division_family_: + elm="" + if printElemNumber: elm="(%d)" % cnt + res+=prefix+("read_division_family%s: %s\n" % (elm, self.DebugFormatString(e))) + cnt+=1 + if self.has_write_division_family_: res+=prefix+("write_division_family: %s\n" % self.DebugFormatString(self.write_division_family_)) return res @@ -3400,6 +3520,8 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): kdefinition = 3 kstate = 4 konly_use_if_required = 6 + kread_division_family = 7 + kwrite_division_family = 8 _TEXT = _BuildTagLookupTable({ 0: "ErrorCode", @@ -3408,7 +3530,9 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): 3: "definition", 4: "state", 6: "only_use_if_required", - }, 6) + 7: "read_division_family", + 8: "write_division_family", + }, 8) _TYPES = _BuildTagLookupTable({ 0: ProtocolBuffer.Encoder.NUMERIC, @@ -3417,12 +3541,368 @@ class CompositeIndex(ProtocolBuffer.ProtocolMessage): 3: ProtocolBuffer.Encoder.STRING, 4: ProtocolBuffer.Encoder.NUMERIC, 6: ProtocolBuffer.Encoder.NUMERIC, - }, 6, ProtocolBuffer.Encoder.MAX_TYPE) + 7: ProtocolBuffer.Encoder.STRING, + 8: ProtocolBuffer.Encoder.STRING, + }, 8, ProtocolBuffer.Encoder.MAX_TYPE) _STYLE = """""" _STYLE_CONTENT_TYPE = """""" _PROTO_DESCRIPTOR_NAME = 'storage_onestore_v3.CompositeIndex' +class SearchIndexEntry(ProtocolBuffer.ProtocolMessage): + has_index_id_ = 0 + index_id_ = 0 + has_fingerprint_1999_ = 0 + fingerprint_1999_ = 0 + has_fingerprint_2011_ = 0 + fingerprint_2011_ = 0 + + def __init__(self, contents=None): + self.division_family_ = [] + if contents is not None: self.MergeFromString(contents) + + def index_id(self): return self.index_id_ + + def set_index_id(self, x): + self.has_index_id_ = 1 + self.index_id_ = x + + def clear_index_id(self): + if self.has_index_id_: + self.has_index_id_ = 0 + self.index_id_ = 0 + + def has_index_id(self): return self.has_index_id_ + + def division_family_size(self): return len(self.division_family_) + def division_family_list(self): return self.division_family_ + + def division_family(self, i): + return self.division_family_[i] + + def set_division_family(self, i, x): + self.division_family_[i] = x + + def add_division_family(self, x): + self.division_family_.append(x) + + def clear_division_family(self): + self.division_family_ = [] + + def fingerprint_1999(self): return self.fingerprint_1999_ + + def set_fingerprint_1999(self, x): + self.has_fingerprint_1999_ = 1 + self.fingerprint_1999_ = x + + def clear_fingerprint_1999(self): + if self.has_fingerprint_1999_: + self.has_fingerprint_1999_ = 0 + self.fingerprint_1999_ = 0 + + def has_fingerprint_1999(self): return self.has_fingerprint_1999_ + + def fingerprint_2011(self): return self.fingerprint_2011_ + + def set_fingerprint_2011(self, x): + self.has_fingerprint_2011_ = 1 + self.fingerprint_2011_ = x + + def clear_fingerprint_2011(self): + if self.has_fingerprint_2011_: + self.has_fingerprint_2011_ = 0 + self.fingerprint_2011_ = 0 + + def has_fingerprint_2011(self): return self.has_fingerprint_2011_ + + + def MergeFrom(self, x): + assert x is not self + if (x.has_index_id()): self.set_index_id(x.index_id()) + for i in xrange(x.division_family_size()): self.add_division_family(x.division_family(i)) + if (x.has_fingerprint_1999()): self.set_fingerprint_1999(x.fingerprint_1999()) + if (x.has_fingerprint_2011()): self.set_fingerprint_2011(x.fingerprint_2011()) + + def Equals(self, x): + if x is self: return 1 + if self.has_index_id_ != x.has_index_id_: return 0 + if self.has_index_id_ and self.index_id_ != x.index_id_: return 0 + if len(self.division_family_) != len(x.division_family_): return 0 + for e1, e2 in zip(self.division_family_, x.division_family_): + if e1 != e2: return 0 + if self.has_fingerprint_1999_ != x.has_fingerprint_1999_: return 0 + if self.has_fingerprint_1999_ and self.fingerprint_1999_ != x.fingerprint_1999_: return 0 + if self.has_fingerprint_2011_ != x.has_fingerprint_2011_: return 0 + if self.has_fingerprint_2011_ and self.fingerprint_2011_ != x.fingerprint_2011_: return 0 + return 1 + + def IsInitialized(self, debug_strs=None): + initialized = 1 + if (not self.has_index_id_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: index_id not set.') + return initialized + + def ByteSize(self): + n = 0 + n += self.lengthVarInt64(self.index_id_) + n += 1 * len(self.division_family_) + for i in xrange(len(self.division_family_)): n += self.lengthString(len(self.division_family_[i])) + if (self.has_fingerprint_1999_): n += 9 + if (self.has_fingerprint_2011_): n += 9 + return n + 1 + + def ByteSizePartial(self): + n = 0 + if (self.has_index_id_): + n += 1 + n += self.lengthVarInt64(self.index_id_) + n += 1 * len(self.division_family_) + for i in xrange(len(self.division_family_)): n += self.lengthString(len(self.division_family_[i])) + if (self.has_fingerprint_1999_): n += 9 + if (self.has_fingerprint_2011_): n += 9 + return n + + def Clear(self): + self.clear_index_id() + self.clear_division_family() + self.clear_fingerprint_1999() + self.clear_fingerprint_2011() + + def OutputUnchecked(self, out): + out.putVarInt32(8) + out.putVarInt64(self.index_id_) + for i in xrange(len(self.division_family_)): + out.putVarInt32(18) + out.putPrefixedString(self.division_family_[i]) + if (self.has_fingerprint_1999_): + out.putVarInt32(25) + out.put64(self.fingerprint_1999_) + if (self.has_fingerprint_2011_): + out.putVarInt32(33) + out.put64(self.fingerprint_2011_) + + def OutputPartial(self, out): + if (self.has_index_id_): + out.putVarInt32(8) + out.putVarInt64(self.index_id_) + for i in xrange(len(self.division_family_)): + out.putVarInt32(18) + out.putPrefixedString(self.division_family_[i]) + if (self.has_fingerprint_1999_): + out.putVarInt32(25) + out.put64(self.fingerprint_1999_) + if (self.has_fingerprint_2011_): + out.putVarInt32(33) + out.put64(self.fingerprint_2011_) + + def TryMerge(self, d): + while d.avail() > 0: + tt = d.getVarInt32() + if tt == 8: + self.set_index_id(d.getVarInt64()) + continue + if tt == 18: + self.add_division_family(d.getPrefixedString()) + continue + if tt == 25: + self.set_fingerprint_1999(d.get64()) + continue + if tt == 33: + self.set_fingerprint_2011(d.get64()) + continue + + + if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError + d.skipData(tt) + + + def __str__(self, prefix="", printElemNumber=0): + res="" + if self.has_index_id_: res+=prefix+("index_id: %s\n" % self.DebugFormatInt64(self.index_id_)) + cnt=0 + for e in self.division_family_: + elm="" + if printElemNumber: elm="(%d)" % cnt + res+=prefix+("division_family%s: %s\n" % (elm, self.DebugFormatString(e))) + cnt+=1 + if self.has_fingerprint_1999_: res+=prefix+("fingerprint_1999: %s\n" % self.DebugFormatFixed64(self.fingerprint_1999_)) + if self.has_fingerprint_2011_: res+=prefix+("fingerprint_2011: %s\n" % self.DebugFormatFixed64(self.fingerprint_2011_)) + return res + + + def _BuildTagLookupTable(sparse, maxtag, default=None): + return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)]) + + kindex_id = 1 + kdivision_family = 2 + kfingerprint_1999 = 3 + kfingerprint_2011 = 4 + + _TEXT = _BuildTagLookupTable({ + 0: "ErrorCode", + 1: "index_id", + 2: "division_family", + 3: "fingerprint_1999", + 4: "fingerprint_2011", + }, 4) + + _TYPES = _BuildTagLookupTable({ + 0: ProtocolBuffer.Encoder.NUMERIC, + 1: ProtocolBuffer.Encoder.NUMERIC, + 2: ProtocolBuffer.Encoder.STRING, + 3: ProtocolBuffer.Encoder.DOUBLE, + 4: ProtocolBuffer.Encoder.DOUBLE, + }, 4, ProtocolBuffer.Encoder.MAX_TYPE) + + + _STYLE = """""" + _STYLE_CONTENT_TYPE = """""" + _PROTO_DESCRIPTOR_NAME = 'storage_onestore_v3.SearchIndexEntry' +class SearchIndexExternalId(ProtocolBuffer.ProtocolMessage): + has_index_id_ = 0 + index_id_ = 0 + has_primary_key_ = 0 + + def __init__(self, contents=None): + self.primary_key_ = Reference() + if contents is not None: self.MergeFromString(contents) + + def index_id(self): return self.index_id_ + + def set_index_id(self, x): + self.has_index_id_ = 1 + self.index_id_ = x + + def clear_index_id(self): + if self.has_index_id_: + self.has_index_id_ = 0 + self.index_id_ = 0 + + def has_index_id(self): return self.has_index_id_ + + def primary_key(self): return self.primary_key_ + + def mutable_primary_key(self): self.has_primary_key_ = 1; return self.primary_key_ + + def clear_primary_key(self):self.has_primary_key_ = 0; self.primary_key_.Clear() + + def has_primary_key(self): return self.has_primary_key_ + + + def MergeFrom(self, x): + assert x is not self + if (x.has_index_id()): self.set_index_id(x.index_id()) + if (x.has_primary_key()): self.mutable_primary_key().MergeFrom(x.primary_key()) + + def Equals(self, x): + if x is self: return 1 + if self.has_index_id_ != x.has_index_id_: return 0 + if self.has_index_id_ and self.index_id_ != x.index_id_: return 0 + if self.has_primary_key_ != x.has_primary_key_: return 0 + if self.has_primary_key_ and self.primary_key_ != x.primary_key_: return 0 + return 1 + + def IsInitialized(self, debug_strs=None): + initialized = 1 + if (not self.has_index_id_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: index_id not set.') + if (not self.has_primary_key_): + initialized = 0 + if debug_strs is not None: + debug_strs.append('Required field: primary_key not set.') + elif not self.primary_key_.IsInitialized(debug_strs): initialized = 0 + return initialized + + def ByteSize(self): + n = 0 + n += self.lengthVarInt64(self.index_id_) + n += self.lengthString(self.primary_key_.ByteSize()) + return n + 2 + + def ByteSizePartial(self): + n = 0 + if (self.has_index_id_): + n += 1 + n += self.lengthVarInt64(self.index_id_) + if (self.has_primary_key_): + n += 1 + n += self.lengthString(self.primary_key_.ByteSizePartial()) + return n + + def Clear(self): + self.clear_index_id() + self.clear_primary_key() + + def OutputUnchecked(self, out): + out.putVarInt32(8) + out.putVarInt64(self.index_id_) + out.putVarInt32(18) + out.putVarInt32(self.primary_key_.ByteSize()) + self.primary_key_.OutputUnchecked(out) + + def OutputPartial(self, out): + if (self.has_index_id_): + out.putVarInt32(8) + out.putVarInt64(self.index_id_) + if (self.has_primary_key_): + out.putVarInt32(18) + out.putVarInt32(self.primary_key_.ByteSizePartial()) + self.primary_key_.OutputPartial(out) + + def TryMerge(self, d): + while d.avail() > 0: + tt = d.getVarInt32() + if tt == 8: + self.set_index_id(d.getVarInt64()) + continue + if tt == 18: + length = d.getVarInt32() + tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length) + d.skip(length) + self.mutable_primary_key().TryMerge(tmp) + continue + + + if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError + d.skipData(tt) + + + def __str__(self, prefix="", printElemNumber=0): + res="" + if self.has_index_id_: res+=prefix+("index_id: %s\n" % self.DebugFormatInt64(self.index_id_)) + if self.has_primary_key_: + res+=prefix+"primary_key <\n" + res+=self.primary_key_.__str__(prefix + " ", printElemNumber) + res+=prefix+">\n" + return res + + + def _BuildTagLookupTable(sparse, maxtag, default=None): + return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)]) + + kindex_id = 1 + kprimary_key = 2 + + _TEXT = _BuildTagLookupTable({ + 0: "ErrorCode", + 1: "index_id", + 2: "primary_key", + }, 2) + + _TYPES = _BuildTagLookupTable({ + 0: ProtocolBuffer.Encoder.NUMERIC, + 1: ProtocolBuffer.Encoder.NUMERIC, + 2: ProtocolBuffer.Encoder.STRING, + }, 2, ProtocolBuffer.Encoder.MAX_TYPE) + + + _STYLE = """""" + _STYLE_CONTENT_TYPE = """""" + _PROTO_DESCRIPTOR_NAME = 'storage_onestore_v3.SearchIndexExternalId' class IndexPostfix_IndexValue(ProtocolBuffer.ProtocolMessage): has_property_name_ = 0 property_name_ = "" @@ -3930,4 +4410,4 @@ class IndexPosition(ProtocolBuffer.ProtocolMessage): if _extension_runtime: pass -__all__ = ['PropertyValue','PropertyValue_ReferenceValuePathElement','PropertyValue_PointValue','PropertyValue_UserValue','PropertyValue_ReferenceValue','Property','Path','Path_Element','Reference','User','EntityProto','CompositeProperty','Index','Index_Property','CompositeIndex','IndexPostfix_IndexValue','IndexPostfix','IndexPosition'] +__all__ = ['PropertyValue','PropertyValue_ReferenceValuePathElement','PropertyValue_PointValue','PropertyValue_UserValue','PropertyValue_ReferenceValue','Property','Path','Path_Element','Reference','User','EntityProto','CompositeProperty','Index','Index_Property','CompositeIndex','SearchIndexEntry','SearchIndexExternalId','IndexPostfix_IndexValue','IndexPostfix','IndexPosition'] diff --git a/python/google/appengine/ext/appstats/static/appstats_js.js b/python/google/appengine/ext/appstats/static/appstats_js.js dissimilarity index 71% index 3562b630..6eba6a9c 100644 --- a/python/google/appengine/ext/appstats/static/appstats_js.js +++ b/python/google/appengine/ext/appstats/static/appstats_js.js @@ -1,87 +1,88 @@ -/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){var f,l=this,aa=function(){},ba=function(a){a.ha=function(){return a.Fb?a.Fb:a.Fb=new a}},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!= -typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return"array"==ca(a)},ea=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"function"==ca(a)},fa=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ka=function(a){return a[ga]|| -(a[ga]=++ja)},ga="closure_uid_"+(1E9*Math.random()>>>0),ja=0,la=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},ma=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.e=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.mc= -function(a,c,g){return b.prototype[c].apply(a,Array.prototype.slice.call(arguments,2))}};var na=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,na);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};p(na,Error);na.prototype.name="CustomError";var oa;var pa=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1")&&(a=a.replace(ua,">"));-1!=a.indexOf('"')&&(a=a.replace(va,"""));-1!=a.indexOf("'")&&(a=a.replace(wa,"'"));-1!=a.indexOf("\x00")&& -(a=a.replace(xa,"�"));return a},sa=/&/g,ta=//g,va=/"/g,wa=/'/g,xa=/\x00/g,ra=/[\x00&<>"']/,za=function(a,b){return ab?1:0};var Aa=function(a,b){b.unshift(a);na.call(this,pa.apply(null,b));b.shift()};p(Aa,na);Aa.prototype.name="AssertionError";var Ba=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new Aa(""+d,e||[]);},r=function(a,b,c){a||Ba("",b,Array.prototype.slice.call(arguments,2))},Ca=function(a,b,c,d){a instanceof b||Ba("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var s=Array.prototype,Da=s.indexOf?function(a,b,c){r(null!=a.length);return s.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var Ka=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},La=function(a,b){for(var c=Ka(a),d=Ia(arguments,1),e=c,g=0;gparseFloat(a))?String(b):a}(),$a={},B=function(a){var b;if(!(b=$a[a])){b=0;for(var c=qa(String(Za)).split("."),d=qa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?c.className=d.join(" "):kb(c,d));2a},ub=function(a,b,c){if(!(a.nodeName in qb))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in rb)b.push(rb[a.nodeName]);else for(a=a.firstChild;a;)ub(a,b,c),a=a.nextSibling},eb=function(a){this.Q=a||l.document||document};f=eb.prototype;f.lb=gb;f.a=function(a){return hb(this.Q,a)}; -f.o=function(a,b,c){return lb(this.Q,arguments)};f.createElement=function(a){return this.Q.createElement(a)};f.createTextNode=function(a){return this.Q.createTextNode(String(a))};f.appendChild=function(a,b){a.appendChild(b)};f.contains=ob; -f.I=function(a){var b;(b="A"==a.tagName||"INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!sb(a)||tb(a)):sb(a)&&tb(a))&&x?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0=a.keyCode)a.keyCode=-1}catch(b){}};var Cb="closure_listenable_"+(1E6*Math.random()|0),Db=0;var Eb=function(a,b,c,d,e){this.W=a;this.Ea=null;this.src=b;this.type=c;this.Fa=!!d;this.Ha=e;this.key=++Db;this.ga=this.Ga=!1},Fb=function(a){a.ga=!0;a.W=null;a.Ea=null;a.src=null;a.Ha=null};var E=function(a){this.src=a;this.m={};this.ua=0};E.prototype.add=function(a,b,c,d,e){var g=a.toString();a=this.m[g];a||(a=this.m[g]=[],this.ua++);var h=Gb(a,b,d,e);-1e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0== -e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||void 0==e.returnValue)e.returnValue=!0}e=[];for(g=c.currentTarget;g;g=g.parentNode)e.push(g);for(var g=a.type,k=e.length-1;!c.$&&0<=k;k--)c.currentTarget=e[k],d&=Sb(e[k],g,!0,c);for(k=0;!c.$&&k>>0),Lb=function(a){r(a,"Listener can not be null.");if(n(a))return a; -r(a.handleEvent,"An object listener must have handleEvent method.");a[Tb]||(a[Tb]=function(b){return a.handleEvent(b)});return a[Tb]};var H=function(a){this.Eb=a;this.Na={}};p(H,zb);var Ub=[];H.prototype.c=function(a,b,c,d){da(b)||(b&&(Ub[0]=b.toString()),b=Ub);for(var e=0;e++d,"infinite loop")}c=this.dc;d=a.type||a;if(m(a))a=new C(a,c);else if(a instanceof C)a.target=a.target||c;else{var e=a;a=new C(d,c);Ta(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.$&&0<=h;h--)g=a.currentTarget=b[h],e=Wb(g,d,!0,a)&&e;a.$||(g=a.currentTarget=c,e=Wb(g,d,!0,a)&&e,a.$||(e=Wb(g,d,!1,a)&&e));if(b)for(h=0;!a.$&&hb||b>hc(this))throw Error("Child component index out of bounds");this.F&&this.q||(this.F={},this.q=[]);if(a.getParent()==this){var d=bc(a);this.F[d]=a;Ga(this.q,a)}else Qa(this.F,bc(a),a);fc(a,this);Ja(this.q,b,0,a);if(a.f&&this.f&&a.getParent()==this)c=this.B(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.d||this.o();c=L(this,b+1);b=this.B();c=c?c.d: -null;if(a.f)throw Error("Component already rendered");a.d||a.o();b?b.insertBefore(a.d,c||null):a.A.Q.body.appendChild(a.d);a.p&&!a.p.f||a.D()}else this.f&&!a.f&&a.d&&a.d.parentNode&&1==a.d.parentNode.nodeType&&a.D()};f.B=function(){return this.d}; -var ic=function(a){if(null==a.Ca){var b=a.f?a.d:a.A.Q.body,c;t:{c=fb(b);if(c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(b,null))){c=c.direction||c.getPropertyValue("direction")||"";break t}c=""}a.Ca="rtl"==(c||(b.currentStyle?b.currentStyle.direction:null)||b.style&&b.style.direction)}return a.Ca};K.prototype.qa=function(a){if(this.f)throw Error("Component already rendered");this.Ca=a}; -var hc=function(a){return a.q?a.q.length:0},ec=function(a,b){var c;a.F&&b?(c=a.F,c=(b in c?c[b]:void 0)||null):c=null;return c},L=function(a,b){return a.q?a.q[b]||null:null},gc=function(a,b,c){a.q&&t(a.q,b,c)},jc=function(a,b){return a.q&&b?Da(a.q,b):-1}; -K.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:bc(a);a=ec(this,c);if(c&&a){var d=this.F;c in d&&delete d[c];Ga(this.q,a);b&&(a.ba(),a.d&&(c=a.d)&&c.parentNode&&c.parentNode.removeChild(c));fc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var kc,lc={nc:"activedescendant",sc:"atomic",tc:"autocomplete",vc:"busy",yc:"checked",Dc:"controls",Fc:"describedby",Ic:"disabled",Kc:"dropeffect",Lc:"expanded",Mc:"flowto",Oc:"grabbed",Sc:"haspopup",Uc:"hidden",Wc:"invalid",Xc:"label",Yc:"labelledby",Zc:"level",dd:"live",od:"multiline",pd:"multiselectable",td:"orientation",ud:"owns",vd:"posinset",xd:"pressed",Bd:"readonly",Dd:"relevant",Ed:"required",Kd:"selected",Md:"setsize",Od:"sort",ae:"valuemax",be:"valuemin",ce:"valuenow",de:"valuetext"};var mc={oc:"alert",pc:"alertdialog",qc:"application",rc:"article",uc:"banner",wc:"button",xc:"checkbox",zc:"columnheader",Ac:"combobox",Bc:"complementary",Cc:"contentinfo",Ec:"definition",Gc:"dialog",Hc:"directory",Jc:"document",Nc:"form",Pc:"grid",Qc:"gridcell",Rc:"group",Tc:"heading",Vc:"img",$c:"link",ad:"list",bd:"listbox",cd:"listitem",ed:"log",fd:"main",gd:"marquee",hd:"math",jd:"menu",kd:"menubar",ld:"menuitem",md:"menuitemcheckbox",nd:"menuitemradio",qd:"navigation",rd:"note",sd:"option", -wd:"presentation",yd:"progressbar",zd:"radio",Ad:"radiogroup",Cd:"region",Fd:"row",Gd:"rowgroup",Hd:"rowheader",Id:"scrollbar",Jd:"search",Ld:"separator",Nd:"slider",Pd:"spinbutton",Qd:"status",Rd:"tab",Sd:"tablist",Td:"tabpanel",Ud:"textbox",Vd:"timer",Wd:"toolbar",Xd:"tooltip",Yd:"tree",Zd:"treegrid",$d:"treeitem"};var nc=function(a,b){b?(r(Pa(mc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},pc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=oc(b);""===c||void 0==c?(kc||(kc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=kc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d, -c)},oc=function(a){r(a,"ARIA attribute cannot be empty.");r(Pa(lc,a),"No such ARIA attribute "+a);return"aria-"+a};var sc=function(a,b,c,d,e){if(!(x||z&&B("525")))return!0;if(A&&e)return qc(a);if(e&&!d)return!1;"number"==typeof b&&(b=rc(b));if(!c&&(17==b||18==b||A&&91==b))return!1;if(z&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(x&&d&&b==a)return!1;switch(a){case 13:return!0;case 27:return!z}return qc(a)},qc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||z&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0; -default:return!1}},rc=function(a){if(y)a=tc(a);else if(A&&z)t:switch(a){case 93:a=91;break t}return a},tc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var M=function(a,b){I.call(this);a&&uc(this,a,b)};p(M,I);f=M.prototype;f.d=null;f.Ia=null;f.Ya=null;f.Ja=null;f.r=-1;f.N=-1;f.kb=!1; -var vc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},wc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},xc=x||z&&B("525"),yc=A&&y; -M.prototype.Sb=function(a){z&&(17==this.r&&!a.ctrlKey||18==this.r&&!a.altKey||A&&91==this.r&&!a.metaKey)&&(this.N=this.r=-1);-1==this.r&&(a.ctrlKey&&17!=a.keyCode?this.r=17:a.altKey&&18!=a.keyCode?this.r=18:a.metaKey&&91!=a.keyCode&&(this.r=91));xc&&!sc(a.keyCode,this.r,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.N=rc(a.keyCode),yc&&(this.kb=a.altKey))};M.prototype.Tb=function(a){this.N=this.r=-1;this.kb=a.altKey}; -M.prototype.handleEvent=function(a){var b=a.O,c,d,e=b.altKey;x&&"keypress"==a.type?(c=this.N,d=13!=c&&27!=c?b.keyCode:0):z&&"keypress"==a.type?(c=this.N,d=0<=b.charCode&&63232>b.charCode&&qc(c)?b.charCode:0):Wa?(c=this.N,d=qc(c)?b.keyCode:0):(c=b.keyCode||this.N,d=b.charCode||0,yc&&(e=this.kb),A&&63==d&&224==c&&(c=191));var g=c=rc(c),h=b.keyIdentifier;c?63232<=c&&c in vc?g=vc[c]:25==c&&a.shiftKey&&(g=9):h&&h in wc&&(g=wc[h]);a=g==this.r;this.r=g;b=new zc(g,d,a,b);b.altKey=e;this.dispatchEvent(b)}; -M.prototype.a=function(){return this.d};var uc=function(a,b,c){a.Ja&&a.detach();a.d=b;a.Ia=F(a.d,"keypress",a,c);a.Ya=F(a.d,"keydown",a.Sb,c,a);a.Ja=F(a.d,"keyup",a.Tb,c,a)};M.prototype.detach=function(){this.Ia&&(G(this.Ia),G(this.Ya),G(this.Ja),this.Ja=this.Ya=this.Ia=null);this.d=null;this.N=this.r=-1};var zc=function(a,b,c,d){D.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(zc,D);var O=function(a){if(a.classList)return a.classList;a=a.className;return m(a)&&a.match(/\S+/g)||[]},Ac=function(a,b){return a.classList?a.classList.contains(b):u(O(a),b)},P=function(a,b){a.classList?a.classList.add(b):Ac(a,b)||(a.className+=0a?c-1:a},0)},ed=function(a){fd(a,function(a,c){return(a+1)%c},a.h)},dd=function(a){fd(a,function(a,c){a--;return 0>a?c-1:a},a.h)},fd=function(a,b,c){c=0>c?jc(a,a.i):c;var d=hc(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=L(a,c);if(g&&g.s()&&g.isEnabled()&&g.l&2){a.Wa(c);break}e++;c=b.call(a,c,d)}};V.prototype.Wa=function(a){ad(this,a)};var gd=function(){};p(gd,R);ba(gd);f=gd.prototype;f.v=function(){return"goog-tab"};f.ea=function(){return"tab"};f.o=function(a){var b=gd.e.o.call(this,a);(a=a.Ua())&&this.Xa(b,a);return b};f.K=function(a,b){b=gd.e.K.call(this,a,b);var c=this.Ua(b);c&&(a.sb=c);a.g&8&&(c=a.getParent())&&n(c.V)&&(a.t(8,!1),c.V(a));return b};f.Ua=function(a){return a.title||""};f.Xa=function(a,b){a&&(a.title=b||"")};var hd=function(a,b,c){S.call(this,a,b||gd.ha(),c);Wc(this,8,!0);this.T|=9};p(hd,S);hd.prototype.Ua=function(){return this.sb};hd.prototype.Xa=function(a){this.Ab().Xa(this.a(),a);this.sb=a};Fc("goog-tab",function(){return new hd(null)});var W=function(){this.Gb="tablist"};p(W,Q);ba(W);W.prototype.v=function(){return"goog-tab-bar"};W.prototype.bb=function(a,b,c){this.Bb||(this.La||id(this),this.Bb=Ra(this.La));var d=this.Bb[b];d?(Jc(a,jd(d)),a.yb=d):W.e.bb.call(this,a,b,c)};W.prototype.ta=function(a){var b=W.e.ta.call(this,a);this.La||id(this);b.push(this.La[a.yb]);return b};var id=function(a){var b=a.v();a.La={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var X=function(a,b,c){a=a||"top";Jc(this,jd(a));this.yb=a;V.call(this,this.L,b||W.ha(),c);kd(this)};p(X,V);f=X.prototype;f.ac=!0;f.H=null;f.D=function(){X.e.D.call(this);kd(this)};f.removeChild=function(a,b){ld(this,a);return X.e.removeChild.call(this,a,b)};f.Wa=function(a){X.e.Wa.call(this,a);this.ac&&this.V(L(this,a))};f.V=function(a){a?Uc(a,!0):this.H&&Uc(this.H,!1)}; -var ld=function(a,b){if(b&&b==a.H){for(var c=jc(a,b),d=c-1;b=L(a,d);d--)if(b.s()&&b.isEnabled()){a.V(b);return}for(c+=1;b=L(a,c);c++)if(b.s()&&b.isEnabled()){a.V(b);return}a.V(null)}};f=X.prototype;f.Zb=function(a){this.H&&this.H!=a.target&&Uc(this.H,!1);this.H=a.target};f.$b=function(a){a.target==this.H&&(this.H=null)};f.Xb=function(a){ld(this,a.target)};f.Yb=function(a){ld(this,a.target)};f.ma=function(){L(this,this.h)||this.C(this.H||L(this,0))}; -var kd=function(a){dc(a).c(a,"select",a.Zb).c(a,"unselect",a.$b).c(a,"disable",a.Xb).c(a,"hide",a.Yb)},jd=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Fc("goog-tab-bar",function(){return new X});var Y=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,nc(a,h.ea()),P(a,"goog-zippy-header"),md(h,a),a&&h.Ob.c(a,"keydown",h.Pb))}I.call(this);this.A=e||gb();this.R=this.A.a(a)||null;this.Aa=this.A.a(d||null);this.da=(this.Qa=n(b)?b:null)||!b?null:this.A.a(b);this.k=!0==c;this.Ob=new H(this);this.qb=new H(this);var h=this;g(this.R);g(this.Aa);this.S(this.k)};p(Y,I);f=Y.prototype;f.Z=!0;f.ea=function(){return"tab"};f.B=function(){return this.da};f.toggle=function(){this.S(!this.k)}; -f.S=function(a){this.da?J(this.da,a):a&&this.Qa&&(this.da=this.Qa());this.da&&P(this.da,"goog-zippy-content");if(this.Aa)J(this.R,!a),J(this.Aa,a);else if(this.R){var b=this.R;a?P(b,"goog-zippy-expanded"):Cc(b,"goog-zippy-expanded");b=this.R;a?Cc(b,"goog-zippy-collapsed"):P(b,"goog-zippy-collapsed");pc(this.R,"expanded",a)}this.k=a;this.dispatchEvent(new nd("toggle",this))};f.pb=function(){return this.Z};f.Pa=function(a){this.Z!=a&&((this.Z=a)?(md(this,this.R),md(this,this.Aa)):this.qb.ab())}; -var md=function(a,b){b&&a.qb.c(b,"click",a.bc)};Y.prototype.Pb=function(a){if(13==a.keyCode||32==a.keyCode)this.toggle(),this.dispatchEvent(new C("action",this)),a.preventDefault(),a.stopPropagation()};Y.prototype.bc=function(){this.toggle();this.dispatchEvent(new C("action",this))};var nd=function(a,b){C.call(this,a,b)};p(nd,C);var Z=function(a,b){this.ob=[];for(var c=ib("span","ae-zippy",hb(document,a)),d=0,e;e=c[d];d++){var g=e.parentNode.parentNode.parentNode;if(void 0!=g.nextElementSibling)g=g.nextElementSibling;else for(g=g.nextSibling;g&&1!=g.nodeType;)g=g.nextSibling;e=new Y(e,g,!1);this.ob.push(e)}this.hc=new od(this.ob,hb(document,b))};Z.prototype.kc=function(){return this.hc};Z.prototype.lc=function(){return this.ob}; -var od=function(a,b){this.va=a;if(this.va.length)for(var c=0,d;d=this.va[c];c++)F(d,"toggle",this.Wb,!1,this);this.Ma=0;this.k=!1;c="ae-toggle ae-plus ae-action";this.va.length||(c+=" ae-disabled");this.P=mb("span",{className:c},"Expand All");F(this.P,"click",this.Vb,!1,this);b&&b.appendChild(this.P)};od.prototype.Vb=function(){this.va.length&&this.S(!this.k)}; -od.prototype.Wb=function(a){a=a.currentTarget;this.Ma=a.k?this.Ma+1:this.Ma-1;a.k!=this.k&&(a.k?(this.k=!0,pd(this,!0)):0==this.Ma&&(this.k=!1,pd(this,!1)))};od.prototype.S=function(a){this.k=a;a=0;for(var b;b=this.va[a];a++)b.k!=this.k&&b.S(this.k);pd(this)}; -var pd=function(a,b){(void 0!==b?b:a.k)?(Na(a.P,"ae-plus"),La(a.P,"ae-minus"),pb(a.P,"Collapse All")):(Na(a.P,"ae-minus"),La(a.P,"ae-plus"),pb(a.P,"Expand All"))},qd=function(a){this.cc=a;this.Db={};var b,c=mb("div",{},b=mb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),mb("div",{className:"goog-tab-bar-clear"}),a=mb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new X;d.K(b);F(d,"select",this.Cb,!1,this);F(d,"unselect",this.Cb,!1,this); -b=0;for(var e;e=this.cc[b];b++)if(e=hb(document,"ae-stats-details-"+e)){var g=ib("h2",null,e)[0],h;h=g;var k=void 0;db&&"innerText"in h?k=h.innerText.replace(/(\r\n|\r|\n)/g,"\n"):(k=[],ub(h,k,!0),k=k.join(""));k=k.replace(/ \xAD /g," ").replace(/\xAD/g,"");k=k.replace(/\u200B/g,"");db||(k=k.replace(/ +/g," "));" "!=k&&(k=k.replace(/^\s*/,""));h=k;g&&g.parentNode&&g.parentNode.removeChild(g);g=new hd(h);this.Db[ka(g)]=e;d.Da(g,!0);a.appendChild(e);0==b?d.V(g):J(e,!1)}hb(document,"bd").appendChild(c)}; -qd.prototype.Cb=function(a){var b=this.Db[ka(a.target)];J(b,"select"==a.type)};ma("ae.Stats.Details.Tabs",qd);ma("goog.ui.Zippy",Y);Y.prototype.setExpanded=Y.prototype.S;ma("ae.Stats.MakeZippys",Z);Z.prototype.getExpandCollapse=Z.prototype.kc;Z.prototype.getZippys=Z.prototype.lc;od.prototype.setExpanded=od.prototype.S;var $=function(){this.cb=[];this.ib=[]},rd=[[5,.2,1],[6,.2,1.2],[5,.25,1.25],[6,.25,1.5],[4,.5,2],[5,.5,2.5],[6,.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],sd=function(a){if(0>=a)return[2,.5,1];for(var b=1;1>a;)a*=10,b/=10;for(;10<=a;)a/=10,b*=10;for(var c=0;c');a.write('
');for(var e=0;e<=b;e++)a.write(''),a.write(''),a.write(" "+e*c+"");a.write("
\n")}; -$.prototype.jc=function(){this.ib=[];var a=sd(this.fb),b=a[0],c=a[1],a=100/a[2];this.write('\n');td(this,b,c,a);for(var d=0;d\n\n")}td(this,b,c,a);this.write("
');0'),this.write(e.label),0"));this.write("");this.write('
');0');this.write('');0'));0 '),this.write(e.zb),this.write(""));0");this.write("
\n");return this.ib.join("")};$.prototype.ic=function(a,b,c,d,e,g){this.fb=Math.max(this.fb,Math.max(b+c,b+d));this.cb.push({label:a,start:b,duration:c,eb:d,zb:e,ia:g})};ma("Gantt",$);$.prototype.add_bar=$.prototype.ic;$.prototype.draw=$.prototype.jc;})(); +/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){var f,l=this,aa=function(){},ba=function(a){a.ia=function(){return a.Eb?a.Eb:a.Eb=new a}},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!= +typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return"array"==ca(a)},ea=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"function"==ca(a)},fa=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ia=function(a){return a[ga]|| +(a[ga]=++ha)},ga="closure_uid_"+(1E9*Math.random()>>>0),ha=0,ja=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},ka=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.e=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.mc= +function(a,c,g){return b.prototype[c].apply(a,Array.prototype.slice.call(arguments,2))}};var la=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,la);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};p(la,Error);la.prototype.name="CustomError";var ma;var na=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1")&&(a=a.replace(sa,">"));-1!=a.indexOf('"')&&(a=a.replace(ta,"""));-1!=a.indexOf("'")&&(a=a.replace(ua,"'"));-1!=a.indexOf("\x00")&& +(a=a.replace(va,"�"));return a},qa=/&/g,ra=//g,ta=/"/g,ua=/'/g,va=/\x00/g,pa=/[\x00&<>"']/,xa=function(a,b){return ab?1:0};var ya=function(a,b){b.unshift(a);la.call(this,na.apply(null,b));b.shift()};p(ya,la);ya.prototype.name="AssertionError";var za=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new ya(""+d,e||[]);},q=function(a,b,c){a||za("",b,Array.prototype.slice.call(arguments,2))},Aa=function(a,b,c,d){a instanceof b||za("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var r=Array.prototype,Ba=r.indexOf?function(a,b,c){q(null!=a.length);return r.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c=arguments.length?r.slice.call(a,b):r.slice.call(a,b,c)};var Ia=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},Ja=function(a,b){for(var c=Ia(a),d=Ga(arguments,1),e=c,g=0;gparseFloat(a))?String(b):a}(),Za={},A=function(a){var b;if(!(b=Za[a])){b=0;for(var c=oa(String(Ya)).split("."),d=oa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?c.className=d.join(" "):jb(c,d));2a},vb=function(a,b,c){if(!(a.nodeName in pb))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in qb)b.push(qb[a.nodeName]);else for(a=a.firstChild;a;)vb(a,b,c),a=a.nextSibling},db= +function(a){this.Q=a||l.document||document};f=db.prototype;f.lb=fb;f.a=function(a){return gb(this.Q,a)};f.o=function(a,b,c){return kb(this.Q,arguments)};f.createElement=function(a){return this.Q.createElement(a)};f.createTextNode=function(a){return this.Q.createTextNode(String(a))};f.appendChild=function(a,b){a.appendChild(b)};f.contains=nb; +f.I=function(a){var b;(b="A"==a.tagName||"INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!rb(a)||sb(a)):tb(a))&&v?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0=a.keyCode)a.keyCode=-1}catch(b){}};var Db="closure_listenable_"+(1E6*Math.random()|0),Eb=0;var Fb=function(a,b,c,d,e){this.W=a;this.Ea=null;this.src=b;this.type=c;this.Fa=!!d;this.Ha=e;this.key=++Eb;this.ha=this.Ga=!1},Gb=function(a){a.ha=!0;a.W=null;a.Ea=null;a.src=null;a.Ha=null};var D=function(a){this.src=a;this.m={};this.ua=0};D.prototype.add=function(a,b,c,d,e){var g=a.toString();a=this.m[g];a||(a=this.m[g]=[],this.ua++);var h=Hb(a,b,d,e);-1e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0== +e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||void 0==e.returnValue)e.returnValue=!0}e=[];for(g=c.currentTarget;g;g=g.parentNode)e.push(g);for(var g=a.type,k=e.length-1;!c.$&&0<=k;k--)c.currentTarget=e[k],d&=Tb(e[k],g,!0,c);for(k=0;!c.$&&k>>0),Mb=function(a){q(a,"Listener can not be null.");if(n(a))return a; +q(a.handleEvent,"An object listener must have handleEvent method.");a[Ub]||(a[Ub]=function(b){return a.handleEvent(b)});return a[Ub]};var G=function(a){this.Db=a;this.Ma={}};p(G,Ab);var Vb=[];G.prototype.c=function(a,b,c,d){da(b)||(b&&(Vb[0]=b.toString()),b=Vb);for(var e=0;e++d,"infinite loop")}c=this.dc;d=a.type||a;if(m(a))a=new B(a,c);else if(a instanceof B)a.target=a.target||c;else{var e=a;a=new B(d,c);Ra(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.$&&0<=h;h--)g=a.currentTarget=b[h],e=Xb(g,d,!0,a)&&e;a.$||(g=a.currentTarget=c,e=Xb(g,d,!0,a)&&e,a.$||(e=Xb(g,d,!1,a)&&e));if(b)for(h=0;!a.$&&hb||b>ic(this))throw Error("Child component index out of bounds");this.F&&this.q||(this.F={},this.q=[]);if(a.getParent()==this){var d=cc(a);this.F[d]=a;Ea(this.q,a)}else Oa(this.F,cc(a),a);gc(a,this);Ha(this.q,b,0,a);if(a.f&&this.f&&a.getParent()==this)c=this.C(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.d||this.o();c=M(this,b+1);b=this.C();c=c?c.d: +null;if(a.f)throw Error("Component already rendered");a.d||a.o();b?b.insertBefore(a.d,c||null):a.A.Q.body.appendChild(a.d);a.p&&!a.p.f||a.D()}else this.f&&!a.f&&a.d&&a.d.parentNode&&1==a.d.parentNode.nodeType&&a.D()};f.C=function(){return this.d}; +var jc=function(a){if(null==a.Ca){var b=a.f?a.d:a.A.Q.body,c;t:{c=eb(b);if(c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(b,null))){c=c.direction||c.getPropertyValue("direction")||"";break t}c=""}a.Ca="rtl"==(c||(b.currentStyle?b.currentStyle.direction:null)||b.style&&b.style.direction)}return a.Ca};L.prototype.ra=function(a){if(this.f)throw Error("Component already rendered");this.Ca=a}; +var ic=function(a){return a.q?a.q.length:0},fc=function(a,b){var c;a.F&&b?(c=a.F,c=(b in c?c[b]:void 0)||null):c=null;return c},M=function(a,b){return a.q?a.q[b]||null:null},hc=function(a,b,c){a.q&&s(a.q,b,c)},kc=function(a,b){return a.q&&b?Ba(a.q,b):-1}; +L.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:cc(a);a=fc(this,c);if(c&&a){var d=this.F;c in d&&delete d[c];Ea(this.q,a);b&&(a.ba(),a.d&&(c=a.d)&&c.parentNode&&c.parentNode.removeChild(c));gc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var lc,mc={nc:"activedescendant",sc:"atomic",tc:"autocomplete",vc:"busy",yc:"checked",Dc:"controls",Fc:"describedby",Ic:"disabled",Kc:"dropeffect",Lc:"expanded",Mc:"flowto",Oc:"grabbed",Sc:"haspopup",Uc:"hidden",Wc:"invalid",Xc:"label",Yc:"labelledby",Zc:"level",dd:"live",od:"multiline",pd:"multiselectable",td:"orientation",ud:"owns",vd:"posinset",xd:"pressed",Bd:"readonly",Dd:"relevant",Ed:"required",Kd:"selected",Md:"setsize",Od:"sort",ae:"valuemax",be:"valuemin",ce:"valuenow",de:"valuetext"};var nc={oc:"alert",pc:"alertdialog",qc:"application",rc:"article",uc:"banner",wc:"button",xc:"checkbox",zc:"columnheader",Ac:"combobox",Bc:"complementary",Cc:"contentinfo",Ec:"definition",Gc:"dialog",Hc:"directory",Jc:"document",Nc:"form",Pc:"grid",Qc:"gridcell",Rc:"group",Tc:"heading",Vc:"img",$c:"link",ad:"list",bd:"listbox",cd:"listitem",ed:"log",fd:"main",gd:"marquee",hd:"math",jd:"menu",kd:"menubar",ld:"menuitem",md:"menuitemcheckbox",nd:"menuitemradio",qd:"navigation",rd:"note",sd:"option", +wd:"presentation",yd:"progressbar",zd:"radio",Ad:"radiogroup",Cd:"region",Fd:"row",Gd:"rowgroup",Hd:"rowheader",Id:"scrollbar",Jd:"search",Ld:"separator",Nd:"slider",Pd:"spinbutton",Qd:"status",Rd:"tab",Sd:"tablist",Td:"tabpanel",Ud:"textbox",Vd:"timer",Wd:"toolbar",Xd:"tooltip",Yd:"tree",Zd:"treegrid",$d:"treeitem"};var oc=function(a,b){b?(q(Na(nc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},qc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=pc(b);""===c||void 0==c?(lc||(lc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=lc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d, +c)},pc=function(a){q(a,"ARIA attribute cannot be empty.");q(Na(mc,a),"No such ARIA attribute "+a);return"aria-"+a};var tc=function(a,b,c,d,e){if(!(v||x&&A("525")))return!0;if(y&&e)return rc(a);if(e&&!d)return!1;"number"==typeof b&&(b=sc(b));if(!c&&(17==b||18==b||y&&91==b))return!1;if(x&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(v&&d&&b==a)return!1;switch(a){case 13:return!0;case 27:return!x}return rc(a)},rc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||x&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0; +default:return!1}},sc=function(a){if(w)a=uc(a);else if(y&&x)t:switch(a){case 93:a=91;break t}return a},uc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var N=function(a,b){H.call(this);a&&vc(this,a,b)};p(N,H);f=N.prototype;f.d=null;f.Ia=null;f.Ya=null;f.Ja=null;f.r=-1;f.N=-1;f.kb=!1; +var wc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},xc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},yc=v||x&&A("525"),zc=y&&w; +N.prototype.Sb=function(a){x&&(17==this.r&&!a.ctrlKey||18==this.r&&!a.altKey||y&&91==this.r&&!a.metaKey)&&(this.N=this.r=-1);-1==this.r&&(a.ctrlKey&&17!=a.keyCode?this.r=17:a.altKey&&18!=a.keyCode?this.r=18:a.metaKey&&91!=a.keyCode&&(this.r=91));yc&&!tc(a.keyCode,this.r,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.N=sc(a.keyCode),zc&&(this.kb=a.altKey))};N.prototype.Tb=function(a){this.N=this.r=-1;this.kb=a.altKey}; +N.prototype.handleEvent=function(a){var b=a.O,c,d,e=b.altKey;v&&"keypress"==a.type?(c=this.N,d=13!=c&&27!=c?b.keyCode:0):x&&"keypress"==a.type?(c=this.N,d=0<=b.charCode&&63232>b.charCode&&rc(c)?b.charCode:0):Va?(c=this.N,d=rc(c)?b.keyCode:0):(c=b.keyCode||this.N,d=b.charCode||0,zc&&(e=this.kb),y&&63==d&&224==c&&(c=191));var g=c=sc(c),h=b.keyIdentifier;c?63232<=c&&c in wc?g=wc[c]:25==c&&a.shiftKey&&(g=9):h&&h in xc&&(g=xc[h]);a=g==this.r;this.r=g;b=new Ac(g,d,a,b);b.altKey=e;this.dispatchEvent(b)}; +N.prototype.a=function(){return this.d};var vc=function(a,b,c){a.Ja&&a.detach();a.d=b;a.Ia=E(a.d,"keypress",a,c);a.Ya=E(a.d,"keydown",a.Sb,c,a);a.Ja=E(a.d,"keyup",a.Tb,c,a)};N.prototype.detach=function(){this.Ia&&(F(this.Ia),F(this.Ya),F(this.Ja),this.Ja=this.Ya=this.Ia=null);this.d=null;this.N=this.r=-1};var Ac=function(a,b,c,d){C.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(Ac,C);var O=function(a){if(a.classList)return a.classList;a=a.className;return m(a)&&a.match(/\S+/g)||[]},Bc=function(a,b){return a.classList?a.classList.contains(b):t(O(a),b)},Cc=function(a,b){a.classList?a.classList.add(b):Bc(a,b)||(a.className+=0a?c-1:a},0)},hd=function(a){id(a,function(a,c){return(a+1)%c},a.h)},gd=function(a){id(a,function(a,c){a--;return 0>a?c-1:a},a.h)},id=function(a,b,c){c=0>c?kc(a,a.i):c;var d=ic(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=M(a,c);if(g&&g.s()&&g.isEnabled()&&g.l&2){a.Wa(c);break}e++;c=b.call(a,c,d)}};V.prototype.Wa=function(a){dd(this,a)};var jd=function(){};p(jd,R);ba(jd);f=jd.prototype;f.v=function(){return"goog-tab"};f.fa=function(){return"tab"};f.o=function(a){var b=jd.e.o.call(this,a);(a=a.Ua())&&this.Xa(b,a);return b};f.K=function(a,b){b=jd.e.K.call(this,a,b);var c=this.Ua(b);c&&(a.sb=c);a.g&8&&(c=a.getParent())&&n(c.V)&&(a.t(8,!1),c.V(a));return b};f.Ua=function(a){return a.title||""};f.Xa=function(a,b){a&&(a.title=b||"")};var kd=function(a,b,c){S.call(this,a,b||jd.ia(),c);Zc(this,8,!0);this.T|=9};p(kd,S);kd.prototype.Ua=function(){return this.sb};kd.prototype.Xa=function(a){this.zb().Xa(this.a(),a);this.sb=a};Hc("goog-tab",function(){return new kd(null)});var W=function(){this.Gb="tablist"};p(W,Q);ba(W);W.prototype.v=function(){return"goog-tab-bar"};W.prototype.bb=function(a,b,c){this.Ab||(this.Ka||ld(this),this.Ab=Pa(this.Ka));var d=this.Ab[b];d?(Lc(a,md(d)),a.xb=d):W.e.bb.call(this,a,b,c)};W.prototype.ta=function(a){var b=W.e.ta.call(this,a);this.Ka||ld(this);b.push(this.Ka[a.xb]);return b};var ld=function(a){var b=a.v();a.Ka={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var X=function(a,b,c){a=a||"top";Lc(this,md(a));this.xb=a;V.call(this,this.L,b||W.ia(),c);nd(this)};p(X,V);f=X.prototype;f.ac=!0;f.H=null;f.D=function(){X.e.D.call(this);nd(this)};f.removeChild=function(a,b){od(this,a);return X.e.removeChild.call(this,a,b)};f.Wa=function(a){X.e.Wa.call(this,a);this.ac&&this.V(M(this,a))};f.V=function(a){a?Xc(a,!0):this.H&&Xc(this.H,!1)}; +var od=function(a,b){if(b&&b==a.H){for(var c=kc(a,b),d=c-1;b=M(a,d);d--)if(b.s()&&b.isEnabled()){a.V(b);return}for(c+=1;b=M(a,c);c++)if(b.s()&&b.isEnabled()){a.V(b);return}a.V(null)}};f=X.prototype;f.Zb=function(a){this.H&&this.H!=a.target&&Xc(this.H,!1);this.H=a.target};f.$b=function(a){a.target==this.H&&(this.H=null)};f.Xb=function(a){od(this,a.target)};f.Yb=function(a){od(this,a.target)};f.na=function(){M(this,this.h)||this.B(this.H||M(this,0))}; +var nd=function(a){ec(a).c(a,"select",a.Zb).c(a,"unselect",a.$b).c(a,"disable",a.Xb).c(a,"hide",a.Yb)},md=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Hc("goog-tab-bar",function(){return new X});var Y=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,oc(a,h.fa()),Cc(a,"goog-zippy-header"),pd(h,a),a&&h.Ob.c(a,"keydown",h.Pb))}H.call(this);this.A=e||fb();this.R=this.A.a(a)||null;this.Aa=this.A.a(d||null);this.ea=(this.Qa=n(b)?b:null)||!b?null:this.A.a(b);this.k=1==c;this.Ob=new G(this);this.qb=new G(this);var h=this;g(this.R);g(this.Aa);this.S(this.k)};p(Y,H);f=Y.prototype;f.Z=!0;f.fa=function(){return"tab"};f.C=function(){return this.ea};f.toggle=function(){this.S(!this.k)}; +f.S=function(a){this.ea?J(this.ea,a):a&&this.Qa&&(this.ea=this.Qa());this.ea&&Cc(this.ea,"goog-zippy-content");if(this.Aa)J(this.R,!a),J(this.Aa,a);else if(this.R){var b=this.R;a?Cc(b,"goog-zippy-expanded"):Ec(b,"goog-zippy-expanded");b=this.R;a?Ec(b,"goog-zippy-collapsed"):Cc(b,"goog-zippy-collapsed");qc(this.R,"expanded",a)}this.k=a;this.dispatchEvent(new qd("toggle",this))};f.pb=function(){return this.Z};f.Pa=function(a){this.Z!=a&&((this.Z=a)?(pd(this,this.R),pd(this,this.Aa)):this.qb.ab())}; +var pd=function(a,b){b&&a.qb.c(b,"click",a.bc)};Y.prototype.Pb=function(a){if(13==a.keyCode||32==a.keyCode)this.toggle(),this.dispatchEvent(new B("action",this)),a.preventDefault(),a.stopPropagation()};Y.prototype.bc=function(){this.toggle();this.dispatchEvent(new B("action",this))};var qd=function(a,b){B.call(this,a,b)};p(qd,B);var Z=function(a,b){this.ob=[];for(var c=hb("span","ae-zippy",gb(document,a)),d=0,e;e=c[d];d++){var g=e.parentNode.parentNode.parentNode;if(void 0!=g.nextElementSibling)g=g.nextElementSibling;else for(g=g.nextSibling;g&&1!=g.nodeType;)g=g.nextSibling;e=new Y(e,g,!1);this.ob.push(e)}this.hc=new rd(this.ob,gb(document,b))};Z.prototype.kc=function(){return this.hc};Z.prototype.lc=function(){return this.ob}; +var rd=function(a,b){this.va=a;if(this.va.length)for(var c=0,d;d=this.va[c];c++)E(d,"toggle",this.Wb,!1,this);this.La=0;this.k=!1;c="ae-toggle ae-plus ae-action";this.va.length||(c+=" ae-disabled");this.P=lb("span",{className:c},"Expand All");E(this.P,"click",this.Vb,!1,this);b&&b.appendChild(this.P)};rd.prototype.Vb=function(){this.va.length&&this.S(!this.k)}; +rd.prototype.Wb=function(a){a=a.currentTarget;this.La=a.k?this.La+1:this.La-1;a.k!=this.k&&(a.k?(this.k=!0,sd(this,!0)):0==this.La&&(this.k=!1,sd(this,!1)))};rd.prototype.S=function(a){this.k=a;a=0;for(var b;b=this.va[a];a++)b.k!=this.k&&b.S(this.k);sd(this)}; +var sd=function(a,b){(void 0!==b?b:a.k)?(La(a.P,"ae-plus"),Ja(a.P,"ae-minus"),ob(a.P,"Collapse All")):(La(a.P,"ae-minus"),Ja(a.P,"ae-plus"),ob(a.P,"Expand All"))},td=function(a){this.cc=a;this.Cb={};var b,c=lb("div",{},b=lb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),lb("div",{className:"goog-tab-bar-clear"}),a=lb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new X;d.K(b);E(d,"select",this.Bb,!1,this);E(d,"unselect",this.Bb,!1,this); +b=0;for(var e;e=this.cc[b];b++)if(e=gb(document,"ae-stats-details-"+e)){var g=hb("h2",null,e)[0],h;h=g;var k=void 0;cb&&"innerText"in h?k=h.innerText.replace(/(\r\n|\r|\n)/g,"\n"):(k=[],vb(h,k,!0),k=k.join(""));k=k.replace(/ \xAD /g," ").replace(/\xAD/g,"");k=k.replace(/\u200B/g,"");cb||(k=k.replace(/ +/g," "));" "!=k&&(k=k.replace(/^\s*/,""));h=k;g&&g.parentNode&&g.parentNode.removeChild(g);g=new kd(h);this.Cb[ia(g)]=e;d.Da(g,!0);a.appendChild(e);0==b?d.V(g):J(e,!1)}gb(document,"bd").appendChild(c)}; +td.prototype.Bb=function(a){var b=this.Cb[ia(a.target)];J(b,"select"==a.type)};ka("ae.Stats.Details.Tabs",td);ka("goog.ui.Zippy",Y);Y.prototype.setExpanded=Y.prototype.S;ka("ae.Stats.MakeZippys",Z);Z.prototype.getExpandCollapse=Z.prototype.kc;Z.prototype.getZippys=Z.prototype.lc;rd.prototype.setExpanded=rd.prototype.S;var $=function(){this.cb=[];this.ib=[]},ud=[[5,.2,1],[6,.2,1.2],[5,.25,1.25],[6,.25,1.5],[4,.5,2],[5,.5,2.5],[6,.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],vd=function(a){if(0>=a)return[2,.5,1];for(var b=1;1>a;)a*=10,b/=10;for(;10<=a;)a/=10,b*=10;for(var c=0;c');a.write('
');for(var e=0;e<=b;e++)a.write(''),a.write(''),a.write(" "+e*c+"");a.write("
\n")}; +$.prototype.jc=function(){this.ib=[];var a=vd(this.fb),b=a[0],c=a[1],a=100/a[2];this.write('\n');wd(this,b,c,a);for(var d=0;d\n\n")}wd(this,b,c,a);this.write("
');0'),this.write(e.label),0"));this.write("");this.write('
');0');this.write('');0'));0 '),this.write(e.yb),this.write(""));0");this.write("
\n");return this.ib.join("")};$.prototype.ic=function(a,b,c,d,e,g){this.fb=Math.max(this.fb,Math.max(b+c,b+d));this.cb.push({label:a,start:b,duration:c,eb:d,yb:e,ja:g})};ka("Gantt",$);$.prototype.add_bar=$.prototype.ic;$.prototype.draw=$.prototype.jc;})(); diff --git a/python/google/appengine/ext/datastore_admin/backup_handler.py b/python/google/appengine/ext/datastore_admin/backup_handler.py index 849d1de1..f74f60c5 100644 --- a/python/google/appengine/ext/datastore_admin/backup_handler.py +++ b/python/google/appengine/ext/datastore_admin/backup_handler.py @@ -495,6 +495,9 @@ def _perform_backup(run_as_a_service, kinds, selected_namespace, else: raise BackupValidationError('Unknown filesystem "%s".' % filesystem) + backup_info = None + job_operation = None + job_name = 'datastore_backup_%s_%%(kind)s' % re.sub(r'[^\w]', '_', backup) try: job_operation = utils.StartOperation('Backup: %s' % backup) diff --git a/python/google/appengine/ext/datastore_admin/backup_pb2.py b/python/google/appengine/ext/datastore_admin/backup_pb2.py index 5ee7f1d9..8b2f4f9f 100644 --- a/python/google/appengine/ext/datastore_admin/backup_pb2.py +++ b/python/google/appengine/ext/datastore_admin/backup_pb2.py @@ -161,6 +161,8 @@ _BACKUP = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=80, serialized_end=220, ) @@ -203,6 +205,8 @@ _BACKUPINFO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=222, serialized_end=303, ) @@ -252,6 +256,8 @@ _KINDBACKUPINFO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=306, serialized_end=446, ) @@ -294,6 +300,8 @@ _ENTITYSCHEMA_TYPE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=547, serialized_end=725, ) @@ -335,6 +343,8 @@ _ENTITYSCHEMA_FIELD = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=727, serialized_end=833, ) @@ -370,6 +380,8 @@ _ENTITYSCHEMA = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=449, serialized_end=1105, ) diff --git a/python/google/appengine/ext/datastore_admin/main.py b/python/google/appengine/ext/datastore_admin/main.py index ace44117..d743dd1a 100644 --- a/python/google/appengine/ext/datastore_admin/main.py +++ b/python/google/appengine/ext/datastore_admin/main.py @@ -41,9 +41,10 @@ from google.appengine.ext.datastore_admin import config from google.appengine.ext.datastore_admin import copy_handler from google.appengine.ext.datastore_admin import delete_handler from google.appengine.ext.datastore_admin import utils -from google.appengine.ext.db import stats from google.appengine.ext.db import metadata +from google.appengine.ext.db import stats from google.appengine.ext.webapp import util +from google.appengine.runtime import apiproxy_errors @@ -278,7 +279,7 @@ class RouteByActionHandler(webapp.RequestHandler): ReadFromKindIters(kind_iter_list) while kind_iter_list: ReadFromKindIters(kind_iter_list) - except datastore_errors.Timeout: + except (datastore_errors.Timeout, apiproxy_errors.DeadlineExceededError): more_kinds = True logging.warning('Failed to retrieve all kinds within deadline.') return sorted(kind_name_set), more_kinds @@ -305,7 +306,7 @@ class RouteByActionHandler(webapp.RequestHandler): kind_name = kind.kind_name if utils.IsKindNameVisible(kind_name): kind_names.append(kind_name) - except datastore_errors.Timeout: + except (datastore_errors.Timeout, apiproxy_errors.DeadlineExceededError): more_kinds = True logging.warning('Failed to retrieve all kinds within deadline.') return kind_names, more_kinds diff --git a/python/google/appengine/ext/mapreduce/static/status.js b/python/google/appengine/ext/mapreduce/static/status.js index cb381186..1b288c06 100644 --- a/python/google/appengine/ext/mapreduce/static/status.js +++ b/python/google/appengine/ext/mapreduce/static/status.js @@ -245,11 +245,18 @@ function getSortedKeys(obj) { return keys; } -// Gets a local datestring from a UNIX timestamp in milliseconds. -function getLocalTimestring(timestamp_ms) { - var when = new Date(); - when.setTime(timestamp_ms); - return when.toLocaleString(); +// Convert milliseconds since the epoch to an ISO8601 datestring. +// Consider using new Date().toISOString() instead (J.S 1.8+) +function getIso8601String(timestamp_ms) { + var time = new Date(); + time.setTime(timestamp_ms); + return '' + + time.getUTCFullYear() + '-' + + leftPadNumber(time.getUTCMonth() + 1, 2, '0') + '-' + + leftPadNumber(time.getUTCDate(), 2, '0') + 'T' + + leftPadNumber(time.getUTCHours(), 2, '0') + ':' + + leftPadNumber(time.getUTCMinutes(), 2, '0') + ':' + + leftPadNumber(time.getUTCSeconds(), 2, '0') + 'Z'; } function leftPadNumber(number, minSize, paddingChar) { @@ -328,7 +335,7 @@ function initJobOverview(jobs, cursor) { var activity = '' + job.active_shards + ' / ' + job.shards + ' shards'; row.append($('').text(activity)) - row.append($('').text(getLocalTimestring(job.start_timestamp_ms))); + row.append($('').text(getIso8601String(job.start_timestamp_ms))); row.append($('').text(getElapsedTimeString( job.start_timestamp_ms, job.updated_timestamp_ms))); @@ -550,7 +557,7 @@ function refreshJobDetail(jobId, detail) { $('
  • ') .append($('').text('Start time')) .append($('').text(': ')) - .append($('').text(getLocalTimestring( + .append($('').text(getIso8601String( detail.start_timestamp_ms))) .appendTo(jobParams); diff --git a/python/google/appengine/tools/adaptive_thread_pool.py b/python/google/appengine/tools/adaptive_thread_pool.py index fac58852..3f730ec0 100644 --- a/python/google/appengine/tools/adaptive_thread_pool.py +++ b/python/google/appengine/tools/adaptive_thread_pool.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Provides thread-pool-like functionality for workers accessing App Engine. The pool adapts to slow or timing out requests by reducing the number of diff --git a/python/google/appengine/tools/api_server.py b/python/google/appengine/tools/api_server.py index 8768d0d0..3b51b04f 100644 --- a/python/google/appengine/tools/api_server.py +++ b/python/google/appengine/tools/api_server.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Serves the stub App Engine APIs (e.g. memcache, datastore) over HTTP. The Remote API protocol is used for communication. @@ -542,7 +538,7 @@ def ParseCommandArguments(args): parser.add_argument('--smtp_allow_tls', action=boolean_action.BooleanAction, const=True, - default=False) + default=True) parser.add_argument('--prospective_search_path', default=None) diff --git a/python/google/appengine/tools/app_engine_web_xml_parser.py b/python/google/appengine/tools/app_engine_web_xml_parser.py index edcee562..288ef5ab 100644 --- a/python/google/appengine/tools/app_engine_web_xml_parser.py +++ b/python/google/appengine/tools/app_engine_web_xml_parser.py @@ -309,7 +309,7 @@ class AppEngineWebXmlParser(object): node, 'disabled-rewriter')] self.app_engine_web_xml.pagespeed = pagespeed - def ProcessClassLoaderConfig(self, node): + def ProcessClassLoaderConfigNode(self, node): for node in xml_parser_utils.GetNodes(node, 'priority-specifier'): entry = PrioritySpecifierEntry() entry.filename = xml_parser_utils.GetAttribute(node, 'filename') @@ -353,6 +353,29 @@ class AppEngineWebXmlParser(object): return self.app_engine_web_xml.auto_id_policy = policy + def ProcessVmHealthCheckNode(self, node): + vm_health_check = VmHealthCheck() + for child in node: + tag = xml_parser_utils.GetTag(child) + if tag == 'enable-health-check': + vm_health_check.enable_health_check = ( + xml_parser_utils.BooleanValue(child.text)) + elif tag == 'host': + vm_health_check.host = child.text + elif tag in ('check-interval-sec', 'healthy-threshold', + 'restart-threshold', 'timeout-sec', 'unhealthy-threshold'): + text = child.text or '' + try: + value = self._PositiveInt(text) + setattr(vm_health_check, tag.replace('-', '_'), value) + except ValueError: + self.errors.append('value for %s must be a positive integer: "%s"' % + (tag, text)) + else: + self.errors.append( + 'unrecognized element within : <%s>' % tag) + self.app_engine_web_xml.vm_health_check = vm_health_check + def CheckScalingConstraints(self): """Checks that at most one type of scaling is enabled.""" scaling_num = sum([x is not None for x in [ @@ -363,6 +386,25 @@ class AppEngineWebXmlParser(object): if scaling_num > 1: self.errors.append('Cannot enable more than one type of scaling') + @staticmethod + def _PositiveInt(text): + """Parse the given text as a positive integer. + + Args: + text: a string that should contain the decimal representation of a + positive integer. + + Returns: + An int that is the parsed value. + + Raises: + ValueError: if text cannot be parsed as a positive integer. + """ + value = int(text) + if value > 0: + return value + raise ValueError('Not a positive integer: %s' % text) + class AppEngineWebXml(ValueMixin): """Organizes and stores data from appengine-web.xml.""" @@ -380,6 +422,7 @@ class AppEngineWebXml(ValueMixin): self.module = None self.system_properties = {} self.vm_settings = {} + self.vm_health_check = None self.env_variables = {} self.instance_class = None self.automatic_scaling = None @@ -558,3 +601,8 @@ class StaticFileInclude(ValueMixin): self.pattern = pattern self.expiration = expiration self.http_headers = http_headers + + +class VmHealthCheck(ValueMixin): + """Instances contain information about VM health check settings.""" + pass diff --git a/python/google/appengine/tools/appcfg.py b/python/google/appengine/tools/appcfg.py index cb261c19..d77e83ca 100644 --- a/python/google/appengine/tools/appcfg.py +++ b/python/google/appengine/tools/appcfg.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Tool for deploying apps to an app server. Currently, the application only uploads new appversions. To do this, it first @@ -3624,7 +3620,8 @@ class AppCfgApp(object): 'ignoring --no_precompilation') self.options.precompilation = True - if appyaml.runtime.startswith('java'): + if (appyaml.runtime.startswith('java') or + appyaml.GetEffectiveRuntime() == 'dart'): self.options.precompilation = False if self.options.precompilation: diff --git a/python/google/appengine/tools/appcfg_java.py b/python/google/appengine/tools/appcfg_java.py index 6e264a3c..6529a9f5 100644 --- a/python/google/appengine/tools/appcfg_java.py +++ b/python/google/appengine/tools/appcfg_java.py @@ -139,7 +139,7 @@ class JavaAppUpdate(object): self.options.no_symlinks = True - java_home, exec_suffix = _JavaHomeAndSuffix() + java_home, exec_suffix = JavaHomeAndSuffix() self.java_command = os.path.join(java_home, 'bin', 'java' + exec_suffix) self.javac_command = os.path.join(java_home, 'bin', 'javac' + exec_suffix) @@ -568,7 +568,7 @@ def _FilesMatching(root, predicate=lambda f: True): return matches -def _JavaHomeAndSuffix(): +def JavaHomeAndSuffix(): """Find the directory that the JDK is installed in. The JDK install directory is expected to have a bin directory that contains diff --git a/python/google/appengine/tools/appengine_rpc.py b/python/google/appengine/tools/appengine_rpc.py index e1af55b4..2f8c7383 100644 --- a/python/google/appengine/tools/appengine_rpc.py +++ b/python/google/appengine/tools/appengine_rpc.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Tool for performing authenticated RPCs against App Engine.""" diff --git a/python/google/appengine/tools/appengine_rpc_httplib2.py b/python/google/appengine/tools/appengine_rpc_httplib2.py index fee123be..2f8ceced 100644 --- a/python/google/appengine/tools/appengine_rpc_httplib2.py +++ b/python/google/appengine/tools/appengine_rpc_httplib2.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Library with a variant of appengine_rpc using httplib2. The httplib2 module offers some of the features in appengine_rpc, with diff --git a/python/google/appengine/tools/boolean_action.py b/python/google/appengine/tools/boolean_action.py index 77a5527d..809b3d11 100644 --- a/python/google/appengine/tools/boolean_action.py +++ b/python/google/appengine/tools/boolean_action.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Provides a flexible way of configuring Boolean flags using argparse. This action behaves like the "store_const" action but allows the flag to accept diff --git a/python/google/appengine/tools/bulkload_client.py b/python/google/appengine/tools/bulkload_client.py index 4b7e8769..65273cef 100644 --- a/python/google/appengine/tools/bulkload_client.py +++ b/python/google/appengine/tools/bulkload_client.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Imports CSV data over HTTP. Usage: diff --git a/python/google/appengine/tools/bulkloader.py b/python/google/appengine/tools/bulkloader.py index 5920e419..07ae8899 100644 --- a/python/google/appengine/tools/bulkloader.py +++ b/python/google/appengine/tools/bulkloader.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Imports data over HTTP. Usage: diff --git a/python/google/appengine/tools/dev-channel-js.js b/python/google/appengine/tools/dev-channel-js.js index e3f253ee..727d3472 100644 --- a/python/google/appengine/tools/dev-channel-js.js +++ b/python/google/appengine/tools/dev-channel-js.js @@ -1718,6 +1718,17 @@ goog.object.set = function(obj, key, value) { goog.object.setIfUndefined = function(obj, key, value) { return key in obj ? obj[key] : obj[key] = value; }; +goog.object.equals = function(a, b) { + if (!goog.array.equals(goog.object.getKeys(a), goog.object.getKeys(b))) { + return!1; + } + for (var k in a) { + if (a[k] !== b[k]) { + return!1; + } + } + return!0; +}; goog.object.clone = function(obj) { var res = {}, key; for (key in obj) { @@ -2143,7 +2154,7 @@ goog.dom.getDocumentHeight_ = function(win) { var doc = win.document, height = 0; if (doc) { var body = doc.body, docEl = doc.documentElement; - if (!body && !docEl) { + if (!docEl || !body) { return 0; } var vh = goog.dom.getViewportSize_(win).height; @@ -3134,7 +3145,6 @@ goog.events.ListenerMap.findListenerIndex_ = function(listenerArray, listener, o } return-1; }; -goog.events.listeners_ = {}; goog.events.LISTENER_MAP_PROP_ = "closure_lm_" + (1E6 * Math.random() | 0); goog.events.onString_ = "on"; goog.events.onStringMap_ = {}; @@ -3470,7 +3480,7 @@ goog.events.EventTarget.prototype.fireListeners = function(type, capture, eventO rv = !1 !== listenerFn.call(listenerHandler, eventObject) && rv; } } - return rv && !1 != eventObject.returnValue_; + return rv && 0 != eventObject.returnValue_; }; goog.events.EventTarget.prototype.getListeners = function(type, capture) { return this.eventTargetListeners_.getListeners(String(type), capture); @@ -5542,18 +5552,18 @@ goog.Uri.prototype.isReadOnly_ = !1; goog.Uri.prototype.ignoreCase_ = !1; goog.Uri.prototype.toString = function() { var out = [], scheme = this.getScheme(); - scheme && out.push(goog.Uri.encodeSpecialChars_(scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_), ":"); + scheme && out.push(goog.Uri.encodeSpecialChars_(scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, !0), ":"); var domain = this.getDomain(); if (domain) { out.push("//"); var userInfo = this.getUserInfo(); - userInfo && out.push(goog.Uri.encodeSpecialChars_(userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_), "@"); - out.push(goog.string.urlEncode(domain)); + userInfo && out.push(goog.Uri.encodeSpecialChars_(userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, !0), "@"); + out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain))); var port = this.getPort(); null != port && out.push(":", String(port)); } var path = this.getPath(); - path && (this.hasDomain() && "/" != path.charAt(0) && out.push("/"), out.push(goog.Uri.encodeSpecialChars_(path, "/" == path.charAt(0) ? goog.Uri.reDisallowedInAbsolutePath_ : goog.Uri.reDisallowedInRelativePath_))); + path && (this.hasDomain() && "/" != path.charAt(0) && out.push("/"), out.push(goog.Uri.encodeSpecialChars_(path, "/" == path.charAt(0) ? goog.Uri.reDisallowedInAbsolutePath_ : goog.Uri.reDisallowedInRelativePath_, !0))); var query = this.getEncodedQuery(); query && out.push("?", query); var fragment = this.getFragment(); @@ -5594,7 +5604,7 @@ goog.Uri.prototype.getScheme = function() { }; goog.Uri.prototype.setScheme = function(newScheme, opt_decode) { this.enforceReadOnly(); - if (this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme) : newScheme) { + if (this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, !0) : newScheme) { this.scheme_ = this.scheme_.replace(/:$/, ""); } return this; @@ -5618,7 +5628,7 @@ goog.Uri.prototype.getDomain = function() { }; goog.Uri.prototype.setDomain = function(newDomain, opt_decode) { this.enforceReadOnly(); - this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain) : newDomain; + this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, !0) : newDomain; return this; }; goog.Uri.prototype.hasDomain = function() { @@ -5648,7 +5658,7 @@ goog.Uri.prototype.getPath = function() { }; goog.Uri.prototype.setPath = function(newPath, opt_decode) { this.enforceReadOnly(); - this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath) : newPath; + this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, !0) : newPath; return this; }; goog.Uri.prototype.hasPath = function() { @@ -5737,16 +5747,24 @@ goog.Uri.removeDotSegments = function(path) { } return path; }; -goog.Uri.decodeOrEmpty_ = function(val) { - return val ? decodeURIComponent(val) : ""; +goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) { + return val ? opt_preserveReserved ? decodeURI(val) : decodeURIComponent(val) : ""; }; -goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra) { - return goog.isString(unescapedPart) ? encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_) : null; +goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra, opt_removeDoubleEncoding) { + if (goog.isString(unescapedPart)) { + var encoded = encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_); + opt_removeDoubleEncoding && (encoded = goog.Uri.removeDoubleEncoding_(encoded)); + return encoded; + } + return null; }; goog.Uri.encodeChar_ = function(ch) { var n = ch.charCodeAt(0); return "%" + (n >> 4 & 15).toString(16) + (n & 15).toString(16); }; +goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) { + return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, "%$1"); +}; goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g; goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g; goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g; diff --git a/python/google/appengine/tools/dev_appserver_apiserver.py b/python/google/appengine/tools/dev_appserver_apiserver.py index 21ad1d3c..34a8b0b3 100644 --- a/python/google/appengine/tools/dev_appserver_apiserver.py +++ b/python/google/appengine/tools/dev_appserver_apiserver.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Helper CGI for Apiserver in the development app server. This is a fake apiserver proxy that does simple transforms on requests that diff --git a/python/google/appengine/tools/dev_appserver_blobimage.py b/python/google/appengine/tools/dev_appserver_blobimage.py index d1551387..a8b3c790 100644 --- a/python/google/appengine/tools/dev_appserver_blobimage.py +++ b/python/google/appengine/tools/dev_appserver_blobimage.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Dispatcher for dynamic image serving requests. Classes: diff --git a/python/google/appengine/tools/dev_appserver_blobstore.py b/python/google/appengine/tools/dev_appserver_blobstore.py index 312925b3..753565d4 100644 --- a/python/google/appengine/tools/dev_appserver_blobstore.py +++ b/python/google/appengine/tools/dev_appserver_blobstore.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Blobstore support classes. Classes: diff --git a/python/google/appengine/tools/dev_appserver_channel.py b/python/google/appengine/tools/dev_appserver_channel.py index 2442f275..fb846bd0 100644 --- a/python/google/appengine/tools/dev_appserver_channel.py +++ b/python/google/appengine/tools/dev_appserver_channel.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Channel support classes. Classes: diff --git a/python/google/appengine/tools/dev_appserver_import_hook.py b/python/google/appengine/tools/dev_appserver_import_hook.py index 5185f8d9..61f3f053 100644 --- a/python/google/appengine/tools/dev_appserver_import_hook.py +++ b/python/google/appengine/tools/dev_appserver_import_hook.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Import hook for dev_appserver.py.""" import dummy_thread diff --git a/python/google/appengine/tools/dev_appserver_index.py b/python/google/appengine/tools/dev_appserver_index.py index a64e7fd4..fb80ee97 100644 --- a/python/google/appengine/tools/dev_appserver_index.py +++ b/python/google/appengine/tools/dev_appserver_index.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Utilities for generating and updating index.yaml.""" diff --git a/python/google/appengine/tools/dev_appserver_login.py b/python/google/appengine/tools/dev_appserver_login.py index 4aee1e70..d72bb863 100644 --- a/python/google/appengine/tools/dev_appserver_login.py +++ b/python/google/appengine/tools/dev_appserver_login.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Helper CGI for logins/logout in the development application server. This CGI has these parameters: diff --git a/python/google/appengine/tools/dev_appserver_main.py b/python/google/appengine/tools/dev_appserver_main.py index cac350ab..fbf6432e 100644 --- a/python/google/appengine/tools/dev_appserver_main.py +++ b/python/google/appengine/tools/dev_appserver_main.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Runs a development application server for an application. %(script)s [options] diff --git a/python/google/appengine/tools/dev_appserver_multiprocess.py b/python/google/appengine/tools/dev_appserver_multiprocess.py index 330a0ed5..0442da2b 100644 --- a/python/google/appengine/tools/dev_appserver_multiprocess.py +++ b/python/google/appengine/tools/dev_appserver_multiprocess.py @@ -14,8 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - """This module adds support for multiple processes in the dev_appserver. Each instance of the application is started as a separate process on a unique diff --git a/python/google/appengine/tools/dev_appserver_oauth.py b/python/google/appengine/tools/dev_appserver_oauth.py index 7f5d3e3f..66aaa7e0 100644 --- a/python/google/appengine/tools/dev_appserver_oauth.py +++ b/python/google/appengine/tools/dev_appserver_oauth.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Helper CGI for OAuth in the development app server.""" diff --git a/python/google/appengine/tools/dev_appserver_upload.py b/python/google/appengine/tools/dev_appserver_upload.py index 98a7fddf..db53c5db 100644 --- a/python/google/appengine/tools/dev_appserver_upload.py +++ b/python/google/appengine/tools/dev_appserver_upload.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Helper CGI for POST uploads. Utility library contains the main logic behind simulating the blobstore diff --git a/python/google/appengine/tools/devappserver2/api_server.py b/python/google/appengine/tools/devappserver2/api_server.py index 494627bf..325d9e14 100644 --- a/python/google/appengine/tools/devappserver2/api_server.py +++ b/python/google/appengine/tools/devappserver2/api_server.py @@ -530,7 +530,7 @@ def test_setup_stubs( mail_smtp_password='', mail_enable_sendmail=False, mail_show_mail_body=False, - mail_allow_tls=False, + mail_allow_tls=True, matcher_prospective_search_path='/dev/null', search_index_path=None, taskqueue_auto_run_tasks=False, diff --git a/python/google/appengine/tools/devappserver2/application_configuration.py b/python/google/appengine/tools/devappserver2/application_configuration.py index 6a544f63..b02317ab 100644 --- a/python/google/appengine/tools/devappserver2/application_configuration.py +++ b/python/google/appengine/tools/devappserver2/application_configuration.py @@ -83,6 +83,7 @@ class ModuleConfiguration(object): from the yaml or xml file should be used. """ self._config_path = config_path + self._forced_app_id = app_id root = os.path.dirname(config_path) self._is_java = os.path.normpath(config_path).endswith( os.sep + 'WEB-INF' + os.sep + 'appengine-web.xml') @@ -97,8 +98,6 @@ class ModuleConfiguration(object): self._app_info_external, files_to_check = self._parse_configuration( self._config_path) - if app_id: - self._app_info_external.application = app_id self._mtimes = self._get_mtimes(files_to_check) self._application = '%s~%s' % (self.partition, self.application_external_name) @@ -322,6 +321,8 @@ class ModuleConfiguration(object): else: with open(configuration_path) as f: config, files = appinfo_includes.ParseAndReturnIncludePaths(f) + if self._forced_app_id: + config.application = self._forced_app_id return config, [configuration_path] + files def _parse_java_configuration(self, app_engine_web_xml_path): diff --git a/python/google/appengine/tools/devappserver2/application_configuration_test.py b/python/google/appengine/tools/devappserver2/application_configuration_test.py index 6316b6c4..7c6983bc 100644 --- a/python/google/appengine/tools/devappserver2/application_configuration_test.py +++ b/python/google/appengine/tools/devappserver2/application_configuration_test.py @@ -19,6 +19,7 @@ import collections from contextlib import contextmanager +import io import os.path import shutil import tempfile @@ -28,6 +29,7 @@ import google import mox from google.appengine.api import appinfo +from google.appengine.api import appinfo_includes from google.appengine.api import backendinfo from google.appengine.api import dispatchinfo from google.appengine.tools.devappserver2 import application_configuration @@ -53,13 +55,17 @@ class TestModuleConfiguration(unittest.TestCase): def setUp(self): self.mox = mox.Mox() - self.mox.StubOutWithMock( - application_configuration.ModuleConfiguration, - '_parse_configuration') + self.mox.StubOutWithMock(appinfo_includes, 'ParseAndReturnIncludePaths') self.mox.StubOutWithMock(os.path, 'getmtime') + application_configuration.open = self._fake_open def tearDown(self): self.mox.UnsetStubs() + del application_configuration.open + + @staticmethod + def _fake_open(unused_filename): + return io.BytesIO() def test_good_app_yaml_configuration(self): automatic_scaling = appinfo.AutomaticScaling(min_pending_latency='1.0s', @@ -82,13 +88,12 @@ class TestModuleConfiguration(unittest.TestCase): inbound_services=['warmup'], env_variables=env_variables, ) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) self.mox.ReplayAll() - config = application_configuration.ModuleConfiguration( - '/appdir/app.yaml') + config = application_configuration.ModuleConfiguration('/appdir/app.yaml') self.mox.VerifyAll() self.assertEqual(os.path.realpath('/appdir'), config.application_root) @@ -124,8 +129,8 @@ class TestModuleConfiguration(unittest.TestCase): threadsafe=False, manual_scaling=manual_scaling, ) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) self.mox.ReplayAll() @@ -153,16 +158,23 @@ class TestModuleConfiguration(unittest.TestCase): version='version', runtime='python27', threadsafe=False) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) + os.path.getmtime('/appdir/app.yaml').AndReturn(20) + os.path.getmtime('/appdir/app.yaml').AndReturn(20) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) self.mox.ReplayAll() config = application_configuration.ModuleConfiguration( '/appdir/app.yaml', 'overriding-app') - self.mox.VerifyAll() self.assertEqual('overriding-app', config.application_external_name) self.assertEqual('dev~overriding-app', config.application) + config.check_for_updates() + self.assertEqual('overriding-app', config.application_external_name) + self.assertEqual('dev~overriding-app', config.application) + self.mox.VerifyAll() def test_check_for_updates_unchanged_mtime(self): info = appinfo.AppInfoExternal( @@ -171,8 +183,8 @@ class TestModuleConfiguration(unittest.TestCase): version='version', runtime='python27', threadsafe=False) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) os.path.getmtime('/appdir/app.yaml').AndReturn(10) @@ -189,17 +201,15 @@ class TestModuleConfiguration(unittest.TestCase): runtime='python27', includes=['/appdir/include.yaml'], threadsafe=False) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn( - (info, ['/appdir/app.yaml', '/appdir/include.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, ['/appdir/include.yaml'])) os.path.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10) os.path.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(10) os.path.getmtime('/appdir/app.yaml').AndReturn(10) os.path.getmtime('/appdir/include.yaml').AndReturn(11) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn( - (info, ['/appdir/app.yaml', '/appdir/include.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, ['/appdir/include.yaml'])) os.path.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10) os.path.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(11) @@ -221,12 +231,12 @@ class TestModuleConfiguration(unittest.TestCase): version='version', runtime='python27', threadsafe=False) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) os.path.getmtime('/appdir/app.yaml').AndReturn(11) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(11) self.mox.ReplayAll() @@ -261,12 +271,12 @@ class TestModuleConfiguration(unittest.TestCase): min_idle_instances=1, max_idle_instances=2)) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info1, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info1, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) os.path.getmtime('/appdir/app.yaml').AndReturn(11) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info2, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info2, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(11) self.mox.ReplayAll() @@ -282,7 +292,7 @@ class TestModuleConfiguration(unittest.TestCase): self.assertFalse(config.threadsafe) self.assertEqual(automatic_scaling1, config.automatic_scaling) - def test_check_for_mutable_changes(self): + def test_check_for_updates_mutable_changes(self): info1 = appinfo.AppInfoExternal( application='app', module='default', @@ -308,12 +318,12 @@ class TestModuleConfiguration(unittest.TestCase): inbound_services=[], ) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info1, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info1, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(10) os.path.getmtime('/appdir/app.yaml').AndReturn(11) - application_configuration.ModuleConfiguration._parse_configuration( - '/appdir/app.yaml').AndReturn((info2, ['/appdir/app.yaml'])) + appinfo_includes.ParseAndReturnIncludePaths(mox.IgnoreArg()).AndReturn( + (info2, [])) os.path.getmtime('/appdir/app.yaml').AndReturn(11) self.mox.ReplayAll() diff --git a/python/google/appengine/tools/devappserver2/devappserver2.py b/python/google/appengine/tools/devappserver2/devappserver2.py index 32693f3d..299ac12a 100644 --- a/python/google/appengine/tools/devappserver2/devappserver2.py +++ b/python/google/appengine/tools/devappserver2/devappserver2.py @@ -541,7 +541,7 @@ def create_command_line_parser(): '--smtp_allow_tls', action=boolean_action.BooleanAction, const=True, - default=False, + default=True, help='Allow TLS to be used when the SMTP server announces TLS support ' '(ignored if --smtp_host is not set)') diff --git a/python/google/appengine/tools/devappserver2/http_runtime.py b/python/google/appengine/tools/devappserver2/http_runtime.py index 89712d18..8e2f9e8b 100644 --- a/python/google/appengine/tools/devappserver2/http_runtime.py +++ b/python/google/appengine/tools/devappserver2/http_runtime.py @@ -113,6 +113,11 @@ class HttpRuntimeProxy(instance.RuntimeProxy): _VALID_START_PROCESS_FLAVORS = [START_PROCESS, START_PROCESS_FILE] + # TODO: Determine if we can always use SIGTERM. + # Set this to True to quit with SIGTERM rather than SIGKILL + + quit_with_sigterm = False + def __init__(self, args, runtime_config_getter, module_configuration, env=None, start_process_flavor=START_PROCESS): """Initializer for HttpRuntimeProxy. @@ -275,7 +280,11 @@ class HttpRuntimeProxy(instance.RuntimeProxy): with self._process_lock: assert self._process, 'module was not running' try: - self._process.kill() + if HttpRuntimeProxy.quit_with_sigterm: + logging.debug('Calling process.terminate on child runtime.') + self._process.terminate() + else: + self._process.kill() except OSError: pass # Mac leaks file descriptors without call to join. Suspect a race diff --git a/python/google/appengine/tools/devappserver2/http_runtime_test.py b/python/google/appengine/tools/devappserver2/http_runtime_test.py index 0af3c84f..f9ecc06e 100644 --- a/python/google/appengine/tools/devappserver2/http_runtime_test.py +++ b/python/google/appengine/tools/devappserver2/http_runtime_test.py @@ -129,14 +129,19 @@ class HttpRuntimeProxyTest(wsgi_test_utils.WSGITestCase): self.mox.StubOutWithMock(http_proxy.HttpProxy, 'wait_for_connection') http_proxy.HttpProxy.wait_for_connection() + self._saved_quit_with_sigterm = ( + http_runtime.HttpRuntimeProxy.quit_with_sigterm) def tearDown(self): shutil.rmtree(self.tmpdir) self.mox.UnsetStubs() + http_runtime.HttpRuntimeProxy.quit_with_sigterm = ( + self._saved_quit_with_sigterm) - def test_start_and_quit(self): + def _test_start_and_quit(self, quit_with_sigterm): ## Test start() # start() + http_runtime.HttpRuntimeProxy.quit_with_sigterm = quit_with_sigterm safe_subprocess.start_process( ['/runtime'], base64.b64encode(self.runtime_config.SerializeToString()), @@ -153,11 +158,20 @@ class HttpRuntimeProxyTest(wsgi_test_utils.WSGITestCase): self.mox.ResetAll() ## Test quit() - self.process.kill() + if quit_with_sigterm: + self.process.terminate() + else: + self.process.kill() self.mox.ReplayAll() self.proxy.quit() self.mox.VerifyAll() + def test_start_and_quit(self): + self._test_start_and_quit(quit_with_sigterm=False) + + def test_start_and_quit_with_sigterm(self): + self._test_start_and_quit(quit_with_sigterm=True) + def test_start_bad_port(self): safe_subprocess.start_process( ['/runtime'], diff --git a/python/google/appengine/tools/devappserver2/php_runtime.py b/python/google/appengine/tools/devappserver2/php_runtime.py index e2510a91..28918156 100644 --- a/python/google/appengine/tools/devappserver2/php_runtime.py +++ b/python/google/appengine/tools/devappserver2/php_runtime.py @@ -33,8 +33,7 @@ from google.appengine.tools.devappserver2 import safe_subprocess _RUNTIME_PATH = os.path.abspath( - os.path.join(os.path.dirname(sys.argv[0]), '_php_runtime.py') - ) + os.path.join(os.path.dirname(sys.argv[0]), '_php_runtime.py')) _CHECK_ENVIRONMENT_SCRIPT_PATH = os.path.join( os.path.dirname(__file__), 'php', 'check_environment.php') _RUNTIME_ARGS = [sys.executable, _RUNTIME_PATH] diff --git a/python/google/appengine/tools/devappserver2/python_runtime.py b/python/google/appengine/tools/devappserver2/python_runtime.py index 7062af22..86fb637b 100644 --- a/python/google/appengine/tools/devappserver2/python_runtime.py +++ b/python/google/appengine/tools/devappserver2/python_runtime.py @@ -26,8 +26,7 @@ from google.appengine.tools.devappserver2 import http_runtime from google.appengine.tools.devappserver2 import instance _RUNTIME_PATH = os.path.abspath( - os.path.join(os.path.dirname(sys.argv[0]), '_python_runtime.py') - ) + os.path.join(os.path.dirname(sys.argv[0]), '_python_runtime.py')) _RUNTIME_ARGS = [sys.executable, _RUNTIME_PATH] diff --git a/python/google/appengine/tools/devappserver2/runtime_config_pb2.py b/python/google/appengine/tools/devappserver2/runtime_config_pb2.py index 1af10180..13ef39f2 100644 --- a/python/google/appengine/tools/devappserver2/runtime_config_pb2.py +++ b/python/google/appengine/tools/devappserver2/runtime_config_pb2.py @@ -197,6 +197,8 @@ _CONFIG = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=88, serialized_end=796, ) @@ -232,6 +234,8 @@ _PHPCONFIG = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=798, serialized_end=863, ) @@ -267,6 +271,8 @@ _PYTHONCONFIG = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=865, serialized_end=925, ) @@ -323,6 +329,8 @@ _CLOUDSQL = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=927, serialized_end=1043, ) @@ -358,6 +366,8 @@ _LIBRARY = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1045, serialized_end=1085, ) @@ -393,6 +403,8 @@ _ENVIRON = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1087, serialized_end=1124, ) @@ -428,6 +440,8 @@ _VMCONFIG = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1126, serialized_end=1228, ) @@ -477,6 +491,8 @@ _DARTCONFIG = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1230, serialized_end=1341, ) diff --git a/python/google/appengine/tools/devappserver2/tee.py b/python/google/appengine/tools/devappserver2/tee.py index 2072f32c..2f6c38fa 100644 --- a/python/google/appengine/tools/devappserver2/tee.py +++ b/python/google/appengine/tools/devappserver2/tee.py @@ -42,6 +42,7 @@ class Tee(threading.Thread): if not line: break self.__out.write(line) + self.__out.flush() self.__deque.append(line) def get_buf(self): diff --git a/python/google/appengine/tools/devappserver2/vm_runtime_proxy.py b/python/google/appengine/tools/devappserver2/vm_runtime_proxy.py index 405c111c..1ecff65c 100644 --- a/python/google/appengine/tools/devappserver2/vm_runtime_proxy.py +++ b/python/google/appengine/tools/devappserver2/vm_runtime_proxy.py @@ -29,6 +29,7 @@ from google.appengine.tools.docker import containers _DOCKER_IMAGE_NAME_FORMAT = '{display}.{module}.{version}' +_DOCKER_CONTAINER_NAME_FORMAT = 'google.appengine.{image_name}.{minor_version}' class Error(Exception): @@ -39,6 +40,10 @@ class InvalidEnvVariableError(Error): """Raised if an environment variable name or value cannot be supported.""" +class VersionError(Error): + """Raised if no version is specified in application configuration file.""" + + def _GetPortToPublish(port): """Checks if given port is available. @@ -136,6 +141,11 @@ class VMRuntimeProxy(instance.RuntimeProxy): def start(self, dockerfile_dir=None): runtime_config = self._runtime_config_getter() + if not self._module_configuration.major_version: + logging.error('Version needs to be specified in your application ' + 'configuration file.') + raise VersionError() + if not dockerfile_dir: dockerfile_dir = self._module_configuration.application_root @@ -199,6 +209,9 @@ class VMRuntimeProxy(instance.RuntimeProxy): self._module_configuration.module_name, self._module_configuration.major_version, runtime_config.instance_id) + container_name = _DOCKER_CONTAINER_NAME_FORMAT.format( + image_name=image_name, + minor_version=self._module_configuration.minor_version) self._container = containers.Container( self._docker_client, containers.ContainerOptions( @@ -211,7 +224,8 @@ class VMRuntimeProxy(instance.RuntimeProxy): environment=environment, volumes={ external_logs_path: {'bind': '/var/log/app_engine'} - } + }, + name=container_name )) self._container.Start() diff --git a/python/google/appengine/tools/devappserver2/vm_runtime_proxy_dart.py b/python/google/appengine/tools/devappserver2/vm_runtime_proxy_dart.py index d4a4e9f2..d2504fda 100644 --- a/python/google/appengine/tools/devappserver2/vm_runtime_proxy_dart.py +++ b/python/google/appengine/tools/devappserver2/vm_runtime_proxy_dart.py @@ -32,7 +32,6 @@ DEBUG_PORT = 5858 VM_SERVICE_PORT = 8181 DEV_MODE = 'dev' DEPLOY_MODE = 'deploy' -DEFAULT_DOCKER_FILE = 'FROM google/appengine-dart' class DartVMRuntimeProxy(instance.RuntimeProxy): @@ -141,14 +140,8 @@ class DartVMRuntimeProxy(instance.RuntimeProxy): ' links to ' + os.readlink(src)) raise - dst_dockerfile = os.path.join(dst_application_dir, 'Dockerfile') dst_build_dir = os.path.join(dst_application_dir, 'build') - # Write default Dockerfile if none found. - if not os.path.exists(dst_dockerfile): - with open(dst_dockerfile, 'w') as fd: - fd.write(DEFAULT_DOCKER_FILE) - if self._is_deployment_mode: # Run 'pub build' to generate assets from web/ directory if necessary. web_dir = os.path.join(application_dir, 'web') diff --git a/python/google/appengine/tools/devappserver2/vm_runtime_proxy_go.py b/python/google/appengine/tools/devappserver2/vm_runtime_proxy_go.py index 7d7b0af1..7942f6c3 100644 --- a/python/google/appengine/tools/devappserver2/vm_runtime_proxy_go.py +++ b/python/google/appengine/tools/devappserver2/vm_runtime_proxy_go.py @@ -30,7 +30,7 @@ from google.appengine.tools.devappserver2 import vm_runtime_proxy DEBUG_PORT = 5858 VM_SERVICE_PORT = 8181 -DEFAULT_DOCKER_FILE = """FROM google/golang +DEFAULT_DOCKER_FILE = """FROM google/golang:1.2.2 ADD . /app RUN /bin/bash /app/_ah/build.sh @@ -112,94 +112,10 @@ class GoVMRuntimeProxy(instance.RuntimeProxy): application_dir = os.path.abspath( self._module_configuration.application_root) - # - copy the application to a new temporary directory (follow symlinks) - # - copy used parts of $GOPATH to the temporary directory - # - copy or create a Dockerfile in the temporary directory - # - build & deploy the docker container - with TempDir('go_deployment_dir') as temp_dir: - dst_deployment_dir = temp_dir - dst_application_dir = temp_dir - try: - _copytree(application_dir, dst_application_dir, - self._module_configuration.skip_files) - except shutil.Error as e: - logging.error('Error copying tree: %s', e) - for src, unused_dst, unused_error in e.args[0]: - if os.path.islink(src): - linkto = os.readlink(src) - if not os.path.exists(linkto): - logging.error('Dangling symlink in Go project. ' - 'Path %s links to %s', src, os.readlink(src)) - raise - except OSError as e: - logging.error('Failed to copy dir: %s', e.strerror) - raise - - extras = go_application.get_app_extras_for_vm( - self._module_configuration) - for dest, src in extras: - try: - dest = os.path.join(dst_deployment_dir, dest) - dirname = os.path.dirname(dest) - if not os.path.exists(dirname): - os.makedirs(dirname) - shutil.copy(src, dest) - except OSError as e: - logging.error('Failed to copy %s to %s', src, dest) - raise - - # Make the _ah subdirectory for the app engine tools. - ah_dir = os.path.join(dst_deployment_dir, '_ah') - try: - os.mkdir(ah_dir) - except OSError as e: - logging.error('Failed to create %s: %s', ah_dir, e.strerror) - raise - - # Copy gab. - try: - gab_dest = os.path.join(ah_dir, 'gab') - shutil.copy(_GO_APP_BUILDER, gab_dest) - except OSError as e: - logging.error('Failed to copy %s to %s', _GO_APP_BUILDER, gab_dest) - raise - - # Write build script. - nobuild_files = '^' + str(self._module_configuration.nobuild_files) - gab_args = [ - '/app/_ah/gab', - '-app_base', '/app', - '-arch', '6', - '-dynamic', - '-goroot', '/goroot', - '-nobuild_files', nobuild_files, - '-unsafe', - '-binary_name', '_ah_exe', - '-work_dir', '/tmp/work', - '-vm', - ] - gab_args.extend( - go_application.list_go_files(self._module_configuration)) - gab_args.extend([x[0] for x in extras]) - dst_build = os.path.join(ah_dir, 'build.sh') - with open(dst_build, 'wb') as fd: - fd.write('#!/bin/bash\n') - fd.write('set -e\n') - fd.write('mkdir -p /tmp/work\n') - fd.write('chmod a+x /app/_ah/gab\n') - # Without this line, Windows errors "text file busy". - fd.write('shasum /app/_ah/gab\n') - fd.write(' '.join(gab_args) + '\n') - fd.write('mv /tmp/work/_ah_exe /app/_ah/exe\n') - fd.write('rm -rf /tmp/work\n') - fd.write('echo Done.\n') - os.chmod(dst_build, 0777) - - # Write default Dockerfile if none found. - dst_dockerfile = os.path.join(dst_application_dir, 'Dockerfile') - if not os.path.exists(dst_dockerfile): - with open(dst_dockerfile, 'w') as fd: - fd.write(DEFAULT_DOCKER_FILE) + with TempDir('go_deployment_dir') as dst_deployment_dir: + build_go_docker_image_source( + application_dir, dst_deployment_dir, + _GO_APP_BUILDER, self._module_configuration) self._vm_runtime_proxy.start(dockerfile_dir=dst_deployment_dir) @@ -215,6 +131,18 @@ class GoVMRuntimeProxy(instance.RuntimeProxy): self._vm_runtime_proxy.quit() +def _write_dockerfile(dst_dir): + """Writes Dockerfile to named directory if one does not exist. + + Args: + dst_dir: string name of destination directory. + """ + dst_dockerfile = os.path.join(dst_dir, 'Dockerfile') + if not os.path.exists(dst_dockerfile): + with open(dst_dockerfile, 'w') as fd: + fd.write(DEFAULT_DOCKER_FILE) + + class TempDir(object): """Creates a temporary directory.""" @@ -255,3 +183,104 @@ def _copytree(src, dst, skip_files, symlinks=False): shutil.copytree(s, d, symlinks, ignore=ignored_files) else: shutil.copy2(s, d) + + +def build_go_docker_image_source( + application_dir, dst_deployment_dir, go_app_builder, module_configuration): + """Builds the Docker image source in preparation for building. + + Steps: + copy the application to dst_deployment_dir (follow symlinks) + copy used parts of $GOPATH to dst_deployment_dir + copy or create a Dockerfile in dst_deployment_dir + + Args: + application_dir: string pathname of application directory. + dst_deployment_dir: string pathname of temporary deployment directory. + go_app_builder: string pathname of docker-gab executable. + module_configuration: An application_configuration.ModuleConfiguration + instance respresenting the configuration of the module that owns the + runtime. + """ + try: + _copytree(application_dir, dst_deployment_dir, + module_configuration.skip_files) + except shutil.Error as e: + logging.error('Error copying tree: %s', e) + for src, unused_dst, unused_error in e.args[0]: + if os.path.islink(src): + linkto = os.readlink(src) + if not os.path.exists(linkto): + logging.error('Dangling symlink in Go project. ' + 'Path %s links to %s', src, os.readlink(src)) + raise + except OSError as e: + logging.error('Failed to copy dir: %s', e.strerror) + raise + + extras = go_application.get_app_extras_for_vm(module_configuration) + for dest, src in extras: + try: + dest = os.path.join(dst_deployment_dir, dest) + dirname = os.path.dirname(dest) + if not os.path.exists(dirname): + os.makedirs(dirname) + shutil.copy(src, dest) + except OSError as e: + logging.error('Failed to copy %s to %s', src, dest) + raise + + # Make the _ah subdirectory for the app engine tools. + ah_dir = os.path.join(dst_deployment_dir, '_ah') + try: + os.mkdir(ah_dir) + except OSError as e: + logging.error('Failed to create %s: %s', ah_dir, e.strerror) + raise + + # Copy gab. + try: + gab_dest = os.path.join(ah_dir, 'gab') + shutil.copy(go_app_builder, gab_dest) + except OSError as e: + logging.error('Failed to copy %s to %s', go_app_builder, gab_dest) + raise + + # Write build script. + nobuild_files = '^' + str(module_configuration.nobuild_files) + gab_args = [ + '/app/_ah/gab', + '-app_base', '/app', + '-arch', '6', + '-dynamic', + '-goroot', '/goroot', + '-nobuild_files', nobuild_files, + '-unsafe', + '-binary_name', '_ah_exe', + '-work_dir', '/tmp/work', + '-vm', + ] + gab_args.extend(go_application.list_go_files(module_configuration)) + gab_args.extend([x[0] for x in extras]) + dst_build = os.path.join(ah_dir, 'build.sh') + lines = [ + '#!/bin/bash', + 'set -e', + 'mkdir -p /tmp/work', + 'chmod a+x /app/_ah/gab', + # Without this line, Windows errors "text file busy". + 'shasum /app/_ah/gab', + ' '.join(gab_args), + 'mv /tmp/work/_ah_exe /app/_ah/exe', + 'rm -rf /tmp/work', + 'echo Done.', + ] + with open(dst_build, 'wb') as fd: + fd.write('\n'.join(lines) + '\n') + os.chmod(dst_build, 0777) + + # TODO: Remove this when classic Go SDK is gone. + # Write default Dockerfile if none found. + _write_dockerfile(dst_deployment_dir) + # Also write the default Dockerfile if not found in the app dir. + _write_dockerfile(application_dir) diff --git a/python/google/appengine/tools/docker/containers.py b/python/google/appengine/tools/docker/containers.py index 2a1bfb59..e9514f28 100644 --- a/python/google/appengine/tools/docker/containers.py +++ b/python/google/appengine/tools/docker/containers.py @@ -28,7 +28,7 @@ Image is a result of "docker build path/to/Dockerfile" command. Container is a result of "docker run image_tag" command. ImageOptions and ContainerOptions allow to pass parameters to these commands. -Versions 1.6 and 1.10 of docker remote API are supported. +Versions 1.9 and 1.10 of docker remote API are supported. """ from collections import namedtuple @@ -51,7 +51,7 @@ class ImageOptions(namedtuple('ImageOptionsT', ['dockerfile_dir', 'tag', 'nocache', 'rm'])): """Options for building Docker Images.""" - def __new__(cls, dockerfile_dir=None, tag=None, nocache=False, rm=False): + def __new__(cls, dockerfile_dir=None, tag=None, nocache=False, rm=True): """This method is redefined to provide default values for namedtuple. Args: @@ -64,7 +64,8 @@ class ImageOptions(namedtuple('ImageOptionsT', nocache: boolean, True if cache should not be used when building the image. rm: boolean, True if intermediate images should be removed after a - successful build. + successful build. Default value is set to True because this is the + default value used by "docker build" command. Returns: ImageOptions object. @@ -216,41 +217,37 @@ class Image(BaseImage): path=self._image_opts.dockerfile_dir, tag=self.tag, quiet=False, fileobj=None, nocache=self._image_opts.nocache, - rm=self._image_opts.rm, stream=False) - - if isinstance(build_res, tuple): - # Older API returns pair (image_id, warnings) - self._id, error = build_res - if not self.id: - raise ImageError( - 'There was a build error for the image %s. Error: %s' % (self.tag, - error)) - else: - # Newer API returns stream_helper generator. Each message contains output - # from the build, and the last message contains the status. - for x in build_res: - x = x.strip() - logging.debug(x) - m = _SUCCESSFUL_BUILD_PATTERN.match(x) - if m: - self._id = m.group(1) - break - else: - # There was no line indicating a successful response. - raise ImageError( - 'There was a build error for the image %s. Error: %s. Run with ' - '\'--verbosity debug\' for more information.' % (self.tag, x)) - if self.id: + rm=self._image_opts.rm) + + log_lines = [x.strip() for x in build_res] + if not log_lines: + logging.error('Error building docker image %s [with no output]', self.tag) + raise ImageError + + m = _SUCCESSFUL_BUILD_PATTERN.match(log_lines[-1]) + + if m: # The build was successful. + self._id = m.group(1) + for line in log_lines: + logging.debug(line) logging.info('Image %s built, id = %s', self.tag, self.id) + else: + logging.error('Error building docker image %s', self.tag) + for line in log_lines: + logging.error(line) + raise ImageError def Remove(self): """Calls "docker rmi".""" if self._id: try: self._docker_client.remove_image(self.id) - except docker.errors.APIError: - logging.warning('Image %s cannot be removed because it is tagged in ' - 'multiple repositories. Use -f to remove it.', self.id) + except docker.errors.APIError as e: + logging.warning('Image %s (id=%s) cannot be removed: %s. Try cleaning ' + 'up old containers that can be listed with ' + '"docker ps -a" and removing the image again with ' + '"docker rmi IMAGE_ID".', + self.tag, self.id, e) self._id = None @@ -364,11 +361,7 @@ class Container(object): if self.id: raise ContainerError('Trying to start already running container.') - try: - self._image.Build() - except ImageError, e: - logging.error('Error starting container: %s', e) - raise + self._image.Build() logging.info('Creating container...') port_bindings = self._container_opts.port_bindings or {} diff --git a/python/google/appengine/tools/download_appstats.py b/python/google/appengine/tools/download_appstats.py index 665b3af3..9c0dbbbb 100644 --- a/python/google/appengine/tools/download_appstats.py +++ b/python/google/appengine/tools/download_appstats.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """Script for downloading Appstats data using remote_api. Usage: diff --git a/python/google/appengine/tools/handler_generator.py b/python/google/appengine/tools/handler_generator.py index dd23d188..bcf877a2 100644 --- a/python/google/appengine/tools/handler_generator.py +++ b/python/google/appengine/tools/handler_generator.py @@ -29,11 +29,11 @@ In this module: static files. """ +from google.appengine.api import appinfo from google.appengine.tools import handler from google.appengine.tools.app_engine_config_exception import AppEngineConfigException API_ENDPOINT_REGEX = '/_ah/spi/*' -MAX_HANDLERS = 100 def GenerateYamlHandlersList(app_engine_web_xml, web_xml, static_files): @@ -47,7 +47,7 @@ def GenerateYamlHandlersList(app_engine_web_xml, web_xml, static_files): handler_length = len(dynamic_handler_generator.GenerateOrderedHandlerList()) if static_files: handler_length += len(static_handler_generator.GenerateOrderedHandlerList()) - if handler_length > MAX_HANDLERS: + if handler_length > appinfo.MAX_URL_MAPS: @@ -88,6 +88,14 @@ def GenerateYamlHandlersListForDevAppServer( A list of strings that together make up the lines of the generated app.yaml file. """ + + + + + + + + appinfo.MAX_URL_MAPS = 10000 static_handler_generator = StaticHandlerGeneratorForDevAppServer( app_engine_web_xml, web_xml, static_urls) dynamic_handler_generator = DynamicHandlerGenerator( diff --git a/python/google/appengine/tools/old_dev_appserver.py b/python/google/appengine/tools/old_dev_appserver.py index 98818c59..c8b1e92c 100644 --- a/python/google/appengine/tools/old_dev_appserver.py +++ b/python/google/appengine/tools/old_dev_appserver.py @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - """Pure-Python application server for testing applications locally. Given a port and the paths to a valid application directory (with an 'app.yaml' diff --git a/python/google/appengine/tools/os_compat.py b/python/google/appengine/tools/os_compat.py index 8aabc77a..50a8ed50 100644 --- a/python/google/appengine/tools/os_compat.py +++ b/python/google/appengine/tools/os_compat.py @@ -14,9 +14,6 @@ # 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 diff --git a/python/google/appengine/tools/remote_api_shell.py b/python/google/appengine/tools/remote_api_shell.py index f6d9ec2c..5d621a04 100644 --- a/python/google/appengine/tools/remote_api_shell.py +++ b/python/google/appengine/tools/remote_api_shell.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """An interactive python shell that uses remote_api. Usage: diff --git a/python/google/appengine/tools/requeue.py b/python/google/appengine/tools/requeue.py index d9a8f03b..db571fc2 100644 --- a/python/google/appengine/tools/requeue.py +++ b/python/google/appengine/tools/requeue.py @@ -14,10 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - - - - """A thread-safe queue in which removed objects put back to the front.""" diff --git a/python/google/appengine/tools/sdk_update_checker.py b/python/google/appengine/tools/sdk_update_checker.py index b3614358..8f9b1119 100644 --- a/python/google/appengine/tools/sdk_update_checker.py +++ b/python/google/appengine/tools/sdk_update_checker.py @@ -70,23 +70,19 @@ class NagFile(validation.Validated): return yaml_object.BuildSingleObject(NagFile, nag_file) -def GetVersionObject(isfile=os.path.isfile, open_fn=open): +def GetVersionObject(): """Gets the version of the SDK by parsing the VERSION file. - Args: - isfile: used for testing. - open_fn: Used for testing. - Returns: A Yaml object or None if the VERSION file does not exist. """ version_filename = os.path.join(os.path.dirname(google.appengine.__file__), VERSION_FILE) - if not isfile(version_filename): + try: + version_fh = open(version_filename) + except IOError: logging.error('Could not find version file at %s', version_filename) return None - - version_fh = open_fn(version_filename, 'r') try: version = yaml.safe_load(version_fh) finally: @@ -133,26 +129,17 @@ class SDKUpdateChecker(object): def __init__(self, rpcserver, - configs, - isdir=os.path.isdir, - isfile=os.path.isfile, - open_fn=open): + configs): """Create a new SDKUpdateChecker. Args: rpcserver: The AbstractRpcServer to use. configs: A list of yaml objects or a single yaml object that specify the configuration of this application. - isdir: Replacement for os.path.isdir (for testing). - isfile: Replacement for os.path.isfile (for testing). - open_fn: Replacement for the open builtin (for testing). """ if not isinstance(configs, list): configs = [configs] self.rpcserver = rpcserver - self.isdir = isdir - self.isfile = isfile - self.open = open_fn self.runtimes = set(config.runtime for config in configs) self.runtime_to_api_version = {} for config in configs: @@ -181,7 +168,7 @@ class SDKUpdateChecker(object): Returns: A Yaml object or None if the file does not exist. """ - return GetVersionObject(isfile=self.isfile, open_fn=self.open) + return GetVersionObject() def CheckSupportedVersion(self): """Determines if the app's api_version is supported by the SDK. @@ -326,14 +313,15 @@ class SDKUpdateChecker(object): A NagFile if the file was present else None. """ nag_filename = SDKUpdateChecker.MakeNagFilename() - if self.isfile(nag_filename): - fh = self.open(nag_filename, 'r') - try: - nag = NagFile.Load(fh) - finally: - fh.close() - return nag - return None + try: + fh = open(nag_filename) + except IOError: + return None + try: + nag = NagFile.Load(fh) + finally: + fh.close() + return nag def _WriteNagFile(self, nag): """Writes the NagFile to the user's nag file. @@ -346,7 +334,7 @@ class SDKUpdateChecker(object): """ nagfilename = SDKUpdateChecker.MakeNagFilename() try: - fh = self.open(nagfilename, 'w') + fh = open(nagfilename, 'w') try: fh.write(nag.ToYAML()) finally: diff --git a/python/google/appengine/tools/yaml_translator.py b/python/google/appengine/tools/yaml_translator.py index cb0258cf..7e7f24f4 100644 --- a/python/google/appengine/tools/yaml_translator.py +++ b/python/google/appengine/tools/yaml_translator.py @@ -262,6 +262,21 @@ class AppYamlTranslator(object): self.SanitizeForYaml(name), self.SanitizeForYaml(settings[name]))) return statements + def TranslateVmHealthCheck(self): + """Translates in appengine-web.xml to yaml.""" + vm_health_check = self.app_engine_web_xml.vm_health_check + if not vm_health_check: + return [] + + statements = ['vm_health_check:'] + for attr in ('enable_health_check', 'check_interval_sec', 'timeout_sec', + 'unhealthy_threshold', 'healthy_threshold', + 'restart_threshold', 'host'): + value = getattr(vm_health_check, attr, None) + if value is not None: + statements.append(' %s: %s' % (attr, value)) + return statements + def TranslateInboundServices(self): services = self.app_engine_web_xml.inbound_services if not services: diff --git a/python/google/net/proto2/proto/descriptor_pb2.py b/python/google/net/proto2/proto/descriptor_pb2.py index 181a348d..0741b698 100644 --- a/python/google/net/proto2/proto/descriptor_pb2.py +++ b/python/google/net/proto2/proto/descriptor_pb2.py @@ -33,7 +33,7 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='net/proto2/proto/descriptor.proto', package='proto2', - serialized_pb=_b('\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xa5\n\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x34\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10java_mutable_api\x18\x1c \x01(\x08:\x05\x66\x61lse\x12+\n#java_multiple_files_mutable_package\x18\x1d \x01(\t\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x12\x65xperimental_style\x18\x1e \x01(\t\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x05\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12%\n\x16\x64\x65precated_raw_message\x18\x0c \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd3\t\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12^\n\tlog_level\x18\x1b \x01(\x0e\x32\x1e.proto2.MethodOptions.LogLevel:+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01\"\x9f\x01\n\x08LogLevel\x12\x0c\n\x08LOG_NONE\x10\x00\x12\x13\n\x0fLOG_HEADER_ONLY\x10\x01\x12/\n+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x10\x02\x12#\n\x1fLOG_HEADER_AND_FILTERED_PAYLOAD\x10\x03\x12\x1a\n\x16LOG_HEADER_AND_PAYLOAD\x10\x04*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe7\x04\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\n \x01(\x08:\x05\x66\x61lse\x12^\n\tlog_level\x18\x0b \x01(\x0e\x32\x1e.proto2.MethodOptions.LogLevel:+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB,\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01\xe0\x01\x01') + serialized_pb=_b('\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xa5\n\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x34\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10java_mutable_api\x18\x1c \x01(\x08:\x05\x66\x61lse\x12+\n#java_multiple_files_mutable_package\x18\x1d \x01(\t\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x12\x65xperimental_style\x18\x1e \x01(\t\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe4\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x05\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12%\n\x16\x64\x65precated_raw_message\x18\x0c \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd3\t\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12^\n\tlog_level\x18\x1b \x01(\x0e\x32\x1e.proto2.MethodOptions.LogLevel:+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01\"\x9f\x01\n\x08LogLevel\x12\x0c\n\x08LOG_NONE\x10\x00\x12\x13\n\x0fLOG_HEADER_ONLY\x10\x01\x12/\n+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x10\x02\x12#\n\x1fLOG_HEADER_AND_FILTERED_PAYLOAD\x10\x03\x12\x1a\n\x16LOG_HEADER_AND_PAYLOAD\x10\x04*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe7\x04\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\n \x01(\x08:\x05\x66\x61lse\x12^\n\tlog_level\x18\x0b \x01(\x0e\x32\x1e.proto2.MethodOptions.LogLevel:+LOG_HEADER_AND_NON_PRIVATE_PAYLOAD_INTERNAL\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB,\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01\xe0\x01\x01') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -224,8 +224,8 @@ _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=4453, - serialized_end=4500, + serialized_start=4472, + serialized_end=4519, ) _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE) @@ -250,8 +250,8 @@ _FIELDOPTIONS_JTYPE = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=4502, - serialized_end=4562, + serialized_start=4521, + serialized_end=4581, ) _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_JTYPE) @@ -276,8 +276,8 @@ _FIELDOPTIONS_JSTYPE = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=4564, - serialized_end=4617, + serialized_start=4583, + serialized_end=4636, ) _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_JSTYPE) @@ -298,8 +298,8 @@ _METHODOPTIONS_PROTOCOL = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=5971, - serialized_end=5999, + serialized_start=5990, + serialized_end=6018, ) _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_PROTOCOL) @@ -328,8 +328,8 @@ _METHODOPTIONS_SECURITYLEVEL = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=6001, - serialized_end=6102, + serialized_start=6020, + serialized_end=6121, ) _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_SECURITYLEVEL) @@ -350,8 +350,8 @@ _METHODOPTIONS_FORMAT = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=6104, - serialized_end=6152, + serialized_start=6123, + serialized_end=6171, ) _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_FORMAT) @@ -384,8 +384,8 @@ _METHODOPTIONS_LOGLEVEL = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=6155, - serialized_end=6314, + serialized_start=6174, + serialized_end=6333, ) _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_LOGLEVEL) @@ -406,8 +406,8 @@ _STREAMOPTIONS_TOKENUNIT = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=6898, - serialized_end=6932, + serialized_start=6917, + serialized_end=6951, ) _sym_db.RegisterEnumDescriptor(_STREAMOPTIONS_TOKENUNIT) @@ -435,6 +435,8 @@ _FILEDESCRIPTORSET = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=45, serialized_end=107, ) @@ -533,6 +535,8 @@ _FILEDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=110, serialized_end=515, ) @@ -568,6 +572,8 @@ _DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=895, serialized_end=939, ) @@ -644,6 +650,8 @@ _DESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=518, serialized_end=939, ) @@ -730,6 +738,8 @@ _FIELDDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=942, serialized_end=1596, ) @@ -758,6 +768,8 @@ _ONEOFDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1598, serialized_end=1634, ) @@ -800,6 +812,8 @@ _ENUMDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1636, serialized_end=1758, ) @@ -842,6 +856,8 @@ _ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1760, serialized_end=1859, ) @@ -891,6 +907,8 @@ _SERVICEDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1862, serialized_end=2035, ) @@ -940,6 +958,8 @@ _METHODDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=2037, serialized_end=2155, ) @@ -989,6 +1009,8 @@ _STREAMDESCRIPTORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=2158, serialized_end=2293, ) @@ -1222,6 +1244,8 @@ _FILEOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], serialized_start=2296, serialized_end=3613, ) @@ -1277,7 +1301,14 @@ _MESSAGEOPTIONS = _descriptor.Descriptor( is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='uninterpreted_option', full_name='proto2.MessageOptions.uninterpreted_option', index=6, + name='map_entry', full_name='proto2.MessageOptions.map_entry', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='proto2.MessageOptions.uninterpreted_option', index=7, number=999, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, @@ -1292,8 +1323,10 @@ _MESSAGEOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], serialized_start=3616, - serialized_end=3953, + serialized_end=3972, ) @@ -1327,8 +1360,10 @@ _FIELDOPTIONS_UPGRADEDOPTION = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], - serialized_start=4406, - serialized_end=4451, + oneofs=[ + ], + serialized_start=4425, + serialized_end=4470, ) _FIELDOPTIONS = _descriptor.Descriptor( @@ -1427,8 +1462,10 @@ _FIELDOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=3956, - serialized_end=4628, + oneofs=[ + ], + serialized_start=3975, + serialized_end=4647, ) @@ -1476,8 +1513,10 @@ _ENUMOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=4631, - serialized_end=4784, + oneofs=[ + ], + serialized_start=4650, + serialized_end=4803, ) @@ -1511,8 +1550,10 @@ _ENUMVALUEOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=4786, - serialized_end=4902, + oneofs=[ + ], + serialized_start=4805, + serialized_end=4921, ) @@ -1560,8 +1601,10 @@ _SERVICEOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=4905, - serialized_end=5087, + oneofs=[ + ], + serialized_start=4924, + serialized_end=5106, ) @@ -1732,8 +1775,10 @@ _METHODOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=5090, - serialized_end=6325, + oneofs=[ + ], + serialized_start=5109, + serialized_end=6344, ) @@ -1845,8 +1890,10 @@ _STREAMOPTIONS = _descriptor.Descriptor( options=None, is_extendable=True, extension_ranges=[(1000, 536870912), ], - serialized_start=6328, - serialized_end=6943, + oneofs=[ + ], + serialized_start=6347, + serialized_end=6962, ) @@ -1880,8 +1927,10 @@ _UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], - serialized_start=7172, - serialized_end=7223, + oneofs=[ + ], + serialized_start=7191, + serialized_end=7242, ) _UNINTERPRETEDOPTION = _descriptor.Descriptor( @@ -1949,8 +1998,10 @@ _UNINTERPRETEDOPTION = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], - serialized_start=6946, - serialized_end=7223, + oneofs=[ + ], + serialized_start=6965, + serialized_end=7242, ) @@ -1998,8 +2049,10 @@ _SOURCECODEINFO_LOCATION = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], - serialized_start=7295, - serialized_end=7394, + oneofs=[ + ], + serialized_start=7314, + serialized_end=7413, ) _SOURCECODEINFO = _descriptor.Descriptor( @@ -2025,8 +2078,10 @@ _SOURCECODEINFO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], - serialized_start=7226, - serialized_end=7394, + oneofs=[ + ], + serialized_start=7245, + serialized_end=7413, ) _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO diff --git a/python/google/net/proto2/python/public/descriptor.py b/python/google/net/proto2/python/public/descriptor.py index c8b4ece3..024024be 100644 --- a/python/google/net/proto2/python/public/descriptor.py +++ b/python/google/net/proto2/python/public/descriptor.py @@ -18,6 +18,7 @@ + """Descriptors essentially contain exactly the information found in a .proto file, in types that make this information accessible in Python. """ @@ -212,13 +213,21 @@ class Descriptor(_NestedDescriptorBase): options: (descriptor_pb2.MessageOptions) Protocol message options or None to use default message options. + oneofs: (list of OneofDescriptor) The list of descriptors for oneof fields + in this message. + oneofs_by_name: (dict str -> OneofDescriptor) Same objects as in |oneofs|, + but indexed by "name" attribute. + file: (FileDescriptor) Reference to file descriptor. """ + + + def __init__(self, name, full_name, filename, containing_type, fields, nested_types, enum_types, extensions, options=None, - is_extendable=True, extension_ranges=None, file=None, - serialized_start=None, serialized_end=None): + is_extendable=True, extension_ranges=None, oneofs=None, + file=None, serialized_start=None, serialized_end=None): """Arguments to __init__() are as described in the description of Descriptor fields above. @@ -259,6 +268,10 @@ class Descriptor(_NestedDescriptorBase): self.extensions_by_name = dict((f.name, f) for f in extensions) self.is_extendable = is_extendable self.extension_ranges = extension_ranges + self.oneofs = oneofs if oneofs is not None else [] + self.oneofs_by_name = dict((o.name, o) for o in self.oneofs) + for oneof in self.oneofs: + oneof.containing_type = self def EnumValueName(self, enum, value): """Returns the string name of an enum value. @@ -344,6 +357,9 @@ class FieldDescriptor(DescriptorBase): options: (descriptor_pb2.FieldOptions) Protocol message field options or None to use default field options. + + containing_oneof: (OneofDescriptor) If the field is a member of a oneof + union, contains its descriptor. Otherwise, None. """ @@ -425,7 +441,7 @@ class FieldDescriptor(DescriptorBase): def __init__(self, name, full_name, index, number, type, cpp_type, label, default_value, message_type, enum_type, containing_type, is_extension, extension_scope, options=None, - has_default_value=True): + has_default_value=True, containing_oneof=None): """The arguments are as described in the description of FieldDescriptor attributes above. @@ -448,6 +464,7 @@ class FieldDescriptor(DescriptorBase): self.enum_type = enum_type self.is_extension = is_extension self.extension_scope = extension_scope + self.containing_oneof = containing_oneof if api_implementation.Type() == 'cpp': if is_extension: if api_implementation.Version() == 2: @@ -566,6 +583,29 @@ class EnumValueDescriptor(DescriptorBase): self.type = type +class OneofDescriptor(object): + """Descriptor for a oneof field. + + name: (str) Name of the oneof field. + full_name: (str) Full name of the oneof field, including package name. + index: (int) 0-based index giving the order of the oneof field inside + its containing type. + containing_type: (Descriptor) Descriptor of the protocol message + type that contains this field. Set by the Descriptor constructor + if we're passed into one. + fields: (list of FieldDescriptor) The list of field descriptors this + oneof can contain. + """ + + def __init__(self, name, full_name, index, containing_type, fields): + """Arguments are as described in the attribute description above.""" + self.name = name + self.full_name = full_name + self.index = index + self.containing_type = containing_type + self.fields = fields + + class ServiceDescriptor(_NestedDescriptorBase): """Descriptor for a service. diff --git a/python/google/net/proto2/python/public/message.py b/python/google/net/proto2/python/public/message.py index e8398b2a..a2d8e40b 100644 --- a/python/google/net/proto2/python/public/message.py +++ b/python/google/net/proto2/python/public/message.py @@ -166,7 +166,11 @@ class Message(object): raise NotImplementedError def ParseFromString(self, serialized): - """Like MergeFromString(), except we clear the object first.""" + """Parse serialized protocol buffer data into this message. + + Like MergeFromString(), except we clear the object first and + do not return the value that MergeFromString returns. + """ self.Clear() self.MergeFromString(serialized) diff --git a/python/google/storage/speckle/proto/client_error_code_pb2.py b/python/google/storage/speckle/proto/client_error_code_pb2.py index 6dc532d8..3e3836cb 100644 --- a/python/google/storage/speckle/proto/client_error_code_pb2.py +++ b/python/google/storage/speckle/proto/client_error_code_pb2.py @@ -208,6 +208,8 @@ _SQLSERVICECLIENTERROR = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=63, serialized_end=1138, ) diff --git a/python/google/storage/speckle/proto/client_pb2.py b/python/google/storage/speckle/proto/client_pb2.py index 68ef46de..3279311e 100644 --- a/python/google/storage/speckle/proto/client_pb2.py +++ b/python/google/storage/speckle/proto/client_pb2.py @@ -624,6 +624,8 @@ _BINDVARIABLEPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=48, serialized_end=230, ) @@ -722,6 +724,8 @@ _RESULTPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=233, serialized_end=629, ) @@ -807,6 +811,8 @@ _OPPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=632, serialized_end=1385, ) @@ -842,6 +848,8 @@ _SAVEPOINT = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1387, serialized_end=1424, ) @@ -891,6 +899,8 @@ _SQLEXCEPTION = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1426, serialized_end=1525, ) @@ -926,6 +936,8 @@ _TUPLEPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1527, serialized_end=1570, ) @@ -1080,6 +1092,8 @@ _COLUMNPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1573, serialized_end=2021, ) @@ -1115,6 +1129,8 @@ _ROWSETPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=2023, serialized_end=2112, ) @@ -1150,6 +1166,8 @@ _JDBCDATABASEMETADATAPROTO_SCHEMA = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=8720, serialized_end=8773, ) @@ -2180,6 +2198,8 @@ _JDBCDATABASEMETADATAPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=2115, serialized_end=9102, ) @@ -2215,6 +2235,8 @@ _PROPERTY = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9104, serialized_end=9142, ) @@ -2327,6 +2349,8 @@ _EXECOPTIONS = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9145, serialized_end=9615, ) @@ -2355,6 +2379,8 @@ _BATCHBINDVARIABLEPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9617, serialized_end=9692, ) @@ -2390,6 +2416,8 @@ _BATCHPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9694, serialized_end=9787, ) @@ -2418,6 +2446,8 @@ _PARAMETERMETADATA = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9789, serialized_end=9822, ) @@ -2453,6 +2483,8 @@ _RPCERRORPROTO = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=9824, serialized_end=9882, ) diff --git a/python/google/storage/speckle/proto/sql_pb2.py b/python/google/storage/speckle/proto/sql_pb2.py index e7bca808..e8ae7c2d 100644 --- a/python/google/storage/speckle/proto/sql_pb2.py +++ b/python/google/storage/speckle/proto/sql_pb2.py @@ -164,6 +164,8 @@ _EXECREQUEST = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=85, serialized_end=481, ) @@ -199,6 +201,8 @@ _EXECRESPONSE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=483, serialized_end=581, ) @@ -248,6 +252,8 @@ _EXECOPREQUEST = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=583, serialized_end=689, ) @@ -311,6 +317,8 @@ _EXECOPRESPONSE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=692, serialized_end=929, ) @@ -367,6 +375,8 @@ _METADATAREQUEST = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=932, serialized_end=1102, ) @@ -409,6 +419,8 @@ _METADATARESPONSE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1105, serialized_end=1275, ) @@ -458,6 +470,8 @@ _OPENCONNECTIONREQUEST = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1278, serialized_end=1450, ) @@ -500,6 +514,8 @@ _OPENCONNECTIONRESPONSE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1453, serialized_end=1587, ) @@ -535,6 +551,8 @@ _CLOSECONNECTIONREQUEST = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1589, serialized_end=1654, ) @@ -563,6 +581,8 @@ _CLOSECONNECTIONRESPONSE = _descriptor.Descriptor( options=None, is_extendable=False, extension_ranges=[], + oneofs=[ + ], serialized_start=1656, serialized_end=1727, ) diff --git a/python/lib/cacerts/urlfetch_cacerts.txt b/python/lib/cacerts/urlfetch_cacerts.txt index 38a912d6..44eab707 100644 --- a/python/lib/cacerts/urlfetch_cacerts.txt +++ b/python/lib/cacerts/urlfetch_cacerts.txt @@ -7200,34 +7200,6 @@ AAOBgQB+HJi+rQONJYXufJCIIiv+J/RCsux/tfxyaAWkfZHvKNF9IDk7eQg3aBhS mIzroG3RNlOQoI8WMB7ew79/RqWVKvnI3jvbd/TyMrEzYaIwNQ== -----END CERTIFICATE----- -subject= /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA -serial=428740A5 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- - subject= /C=IT/L=Milan/O=Actalis S.p.A./03358520967/CN=Actalis Authentication CA G2 serial=07276202 -----BEGIN CERTIFICATE----- diff --git a/python/lib/enum/enum/LICENSE b/python/lib/deprecated_enum/deprecated_enum/LICENSE similarity index 100% rename from python/lib/enum/enum/LICENSE rename to python/lib/deprecated_enum/deprecated_enum/LICENSE diff --git a/python/lib/enum/enum/__init__.py b/python/lib/deprecated_enum/deprecated_enum/__init__.py similarity index 100% rename from python/lib/enum/enum/__init__.py rename to python/lib/deprecated_enum/deprecated_enum/__init__.py diff --git a/python/lib/enum/enum/test/test_enum.py b/python/lib/deprecated_enum/deprecated_enum/test/test_enum.py similarity index 100% rename from python/lib/enum/enum/test/test_enum.py rename to python/lib/deprecated_enum/deprecated_enum/test/test_enum.py diff --git a/python/lib/enum/enum/test/tools.py b/python/lib/deprecated_enum/deprecated_enum/test/tools.py similarity index 100% rename from python/lib/enum/enum/test/tools.py rename to python/lib/deprecated_enum/deprecated_enum/test/tools.py diff --git a/python/lib/distutils/distutils/__init__.py b/python/lib/distutils/distutils/__init__.py index a849f1a9..6e508855 100644 --- a/python/lib/distutils/distutils/__init__.py +++ b/python/lib/distutils/distutils/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.2" +__version__ = "2.7.6" #--end constants-- diff --git a/python/lib/distutils/distutils/ccompiler.py b/python/lib/distutils/distutils/ccompiler.py index c2b1f6fb..4907a0aa 100644 --- a/python/lib/distutils/distutils/ccompiler.py +++ b/python/lib/distutils/distutils/ccompiler.py @@ -17,58 +17,8 @@ from distutils.dir_util import mkpath from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log - -_sysconfig = __import__('sysconfig') - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - _sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', - 'ARFLAGS') - - if 'CC' in os.environ: - cc = os.environ['CC'] - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = so_ext +# following import is for backward compatibility +from distutils.sysconfig import customize_compiler class CCompiler: """Abstract base class to define the interface that must be implemented diff --git a/python/lib/distutils/distutils/command/__init__.py b/python/lib/distutils/distutils/command/__init__.py new file mode 100644 index 00000000..20b159f7 --- /dev/null +++ b/python/lib/distutils/distutils/command/__init__.py @@ -0,0 +1,33 @@ +"""distutils.command + +Package containing implementation of all the standard Distutils +commands.""" + +__revision__ = "$Id$" + +__all__ = ['build', + 'build_py', + 'build_ext', + 'build_clib', + 'build_scripts', + 'clean', + 'install', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + 'sdist', + 'register', + 'bdist', + 'bdist_dumb', + 'bdist_rpm', + 'bdist_wininst', + 'upload', + 'check', + # These two are reserved for future use: + #'bdist_sdux', + #'bdist_pkgtool', + # Note: + # bdist_packager is not included because it only provides + # an abstract base class + ] diff --git a/python/lib/distutils/distutils/command/bdist.py b/python/lib/distutils/distutils/command/bdist.py new file mode 100644 index 00000000..d7910b14 --- /dev/null +++ b/python/lib/distutils/distutils/command/bdist.py @@ -0,0 +1,146 @@ +"""distutils.command.bdist + +Implements the Distutils 'bdist' command (create a built [binary] +distribution).""" + +__revision__ = "$Id$" + +import os + +from distutils.util import get_platform +from distutils.core import Command +from distutils.errors import DistutilsPlatformError, DistutilsOptionError + + +def show_formats(): + """Print list of available formats (arguments to "--format" option). + """ + from distutils.fancy_getopt import FancyGetopt + formats = [] + for format in bdist.format_commands: + formats.append(("formats=" + format, None, + bdist.format_command[format][1])) + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help("List of available distribution formats:") + + +class bdist(Command): + + description = "create a built (binary) distribution" + + user_options = [('bdist-base=', 'b', + "temporary directory for creating built distributions"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('formats=', None, + "formats for distribution (comma-separated list)"), + ('dist-dir=', 'd', + "directory to put final built distributions in " + "[default: dist]"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['skip-build'] + + help_options = [ + ('help-formats', None, + "lists available distribution formats", show_formats), + ] + + # The following commands do not take a format option from bdist + no_format_option = ('bdist_rpm',) + + # This won't do in reality: will need to distinguish RPM-ish Linux, + # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. + default_format = {'posix': 'gztar', + 'nt': 'zip', + 'os2': 'zip'} + + # Establish the preferred order (for the --help-formats option). + format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', + 'wininst', 'zip', 'msi'] + + # And the real information. + format_command = {'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), + 'msi': ('bdist_msi', "Microsoft Installer") + } + + + def initialize_options(self): + self.bdist_base = None + self.plat_name = None + self.formats = None + self.dist_dir = None + self.skip_build = 0 + self.group = None + self.owner = None + + def finalize_options(self): + # have to finalize 'plat_name' before 'bdist_base' + if self.plat_name is None: + if self.skip_build: + self.plat_name = get_platform() + else: + self.plat_name = self.get_finalized_command('build').plat_name + + # 'bdist_base' -- parent of per-built-distribution-format + # temporary directories (eg. we'll probably have + # "build/bdist./dumb", "build/bdist./rpm", etc.) + if self.bdist_base is None: + build_base = self.get_finalized_command('build').build_base + self.bdist_base = os.path.join(build_base, + 'bdist.' + self.plat_name) + + self.ensure_string_list('formats') + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError, \ + "don't know how to create built distributions " + \ + "on platform %s" % os.name + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # Figure out which sub-commands we need to run. + commands = [] + for format in self.formats: + try: + commands.append(self.format_command[format][0]) + except KeyError: + raise DistutilsOptionError, "invalid format '%s'" % format + + # Reinitialize and run each command. + for i in range(len(self.formats)): + cmd_name = commands[i] + sub_cmd = self.reinitialize_command(cmd_name) + if cmd_name not in self.no_format_option: + sub_cmd.format = self.formats[i] + + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + + # If we're going to need to run this command again, tell it to + # keep its temporary files around so subsequent runs go faster. + if cmd_name in commands[i+1:]: + sub_cmd.keep_temp = 1 + self.run_command(cmd_name) diff --git a/python/lib/distutils/distutils/command/bdist_dumb.py b/python/lib/distutils/distutils/command/bdist_dumb.py new file mode 100644 index 00000000..2f3c6682 --- /dev/null +++ b/python/lib/distutils/distutils/command/bdist_dumb.py @@ -0,0 +1,133 @@ +"""distutils.command.bdist_dumb + +Implements the Distutils 'bdist_dumb' command (create a "dumb" built +distribution -- i.e., just an archive to be unpacked under $prefix or +$exec_prefix).""" + +__revision__ = "$Id$" + +import os + +from sysconfig import get_python_version + +from distutils.util import get_platform +from distutils.core import Command +from distutils.dir_util import remove_tree, ensure_relative +from distutils.errors import DistutilsPlatformError +from distutils import log + +class bdist_dumb (Command): + + description = 'create a "dumb" built distribution' + + user_options = [('bdist-dir=', 'd', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('format=', 'f', + "archive format to create (tar, ztar, gztar, zip)"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('relative', None, + "build the archive using relative paths" + "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['keep-temp', 'skip-build', 'relative'] + + default_format = { 'posix': 'gztar', + 'nt': 'zip', + 'os2': 'zip' } + + + def initialize_options (self): + self.bdist_dir = None + self.plat_name = None + self.format = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = None + self.relative = 0 + self.owner = None + self.group = None + + def finalize_options(self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'dumb') + + if self.format is None: + try: + self.format = self.default_format[os.name] + except KeyError: + raise DistutilsPlatformError, \ + ("don't know how to create dumb built distributions " + + "on platform %s") % os.name + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + log.info("installing to %s" % self.bdist_dir) + self.run_command('install') + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.%s" % (self.distribution.get_fullname(), + self.plat_name) + + # OS/2 objects to any ":" characters in a filename (such as when + # a timestamp is used in a version) so change them to hyphens. + if os.name == "os2": + archive_basename = archive_basename.replace(":", "-") + + pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) + if not self.relative: + archive_root = self.bdist_dir + else: + if (self.distribution.has_ext_modules() and + (install.install_base != install.install_platbase)): + raise DistutilsPlatformError, \ + ("can't make a dumb built distribution where " + "base and platbase are different (%s, %s)" + % (repr(install.install_base), + repr(install.install_platbase))) + else: + archive_root = os.path.join(self.bdist_dir, + ensure_relative(install.install_base)) + + # Make the archive + filename = self.make_archive(pseudoinstall_root, + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_dumb', pyversion, + filename)) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/python/lib/distutils/distutils/command/bdist_msi.py b/python/lib/distutils/distutils/command/bdist_msi.py new file mode 100644 index 00000000..703f873b --- /dev/null +++ b/python/lib/distutils/distutils/command/bdist_msi.py @@ -0,0 +1,742 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2005, 2006 Martin von Löwis +# Licensed to PSF under a Contributor Agreement. +# The bdist_wininst command proper +# based on bdist_wininst +""" +Implements the bdist_msi command. +""" +import sys, os +from sysconfig import get_python_version + +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils.version import StrictVersion +from distutils.errors import DistutilsOptionError +from distutils import log +from distutils.util import get_platform + +import msilib +from msilib import schema, sequence, text +from msilib import Directory, Feature, Dialog, add_data + +class PyDialog(Dialog): + """Dialog class with a fixed layout: controls at the top, then a ruler, + then a list of buttons: back, next, cancel. Optionally a bitmap at the + left.""" + def __init__(self, *args, **kw): + """Dialog(database, name, x, y, w, h, attributes, title, first, + default, cancel, bitmap=true)""" + Dialog.__init__(self, *args) + ruler = self.h - 36 + #if kw.get("bitmap", True): + # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") + self.line("BottomLine", 0, ruler, self.w, 0) + + def title(self, title): + "Set the title text of the dialog at the top." + # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, + # text, in VerdanaBold10 + self.text("Title", 15, 10, 320, 60, 0x30003, + r"{\VerdanaBold10}%s" % title) + + def back(self, title, next, name = "Back", active = 1): + """Add a back button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) + + def cancel(self, title, next, name = "Cancel", active = 1): + """Add a cancel button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) + + def next(self, title, next, name = "Next", active = 1): + """Add a Next button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) + + def xbutton(self, name, title, next, xpos): + """Add a button with a given title, the tab-next button, + its name in the Control table, giving its x position; the + y-position is aligned with the other buttons. + + Return the button, so that events can be associated""" + return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) + +class bdist_msi (Command): + + description = "create a Microsoft Installer (.msi) binary distribution" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized)" + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after" + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', + '2.5', '2.6', '2.7', '2.8', '2.9', + '3.0', '3.1', '3.2', '3.3', '3.4', + '3.5', '3.6', '3.7', '3.8', '3.9'] + other_version = 'X' + + def initialize_options (self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.versions = None + + def finalize_options (self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'msi') + + short_version = get_python_version() + if (not self.target_version) and self.distribution.has_ext_modules(): + self.target_version = short_version + + if self.target_version: + self.versions = [self.target_version] + if not self.skip_build and self.distribution.has_ext_modules()\ + and self.target_version != short_version: + raise DistutilsOptionError, \ + "target version can only be %s, or the '--skip-build'" \ + " option must be specified" % (short_version,) + else: + self.versions = list(self.all_versions) + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.pre_install_script: + raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError, \ + "install_script '%s' not found in scripts" % \ + self.install_script + self.install_script_key = None + # finalize_options() + + + def run (self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.prefix = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = sys.version[0:3] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + self.mkpath(self.dist_dir) + fullname = self.distribution.get_fullname() + installer_name = self.get_installer_filename(fullname) + installer_name = os.path.abspath(installer_name) + if os.path.exists(installer_name): os.unlink(installer_name) + + metadata = self.distribution.metadata + author = metadata.author + if not author: + author = metadata.maintainer + if not author: + author = "UNKNOWN" + version = metadata.get_version() + # ProductVersion must be strictly numeric + # XXX need to deal with prerelease versions + sversion = "%d.%d.%d" % StrictVersion(version).version + # Prefix ProductName with Python x.y, so that + # it sorts together with the other Python packages + # in Add-Remove-Programs (APR) + fullname = self.distribution.get_fullname() + if self.target_version: + product_name = "Python %s %s" % (self.target_version, fullname) + else: + product_name = "Python %s" % (fullname) + self.db = msilib.init_database(installer_name, schema, + product_name, msilib.gen_uuid(), + sversion, author) + msilib.add_tables(self.db, sequence) + props = [('DistVersion', version)] + email = metadata.author_email or metadata.maintainer_email + if email: + props.append(("ARPCONTACT", email)) + if metadata.url: + props.append(("ARPURLINFOABOUT", metadata.url)) + if props: + add_data(self.db, 'Property', props) + + self.add_find_python() + self.add_files() + self.add_scripts() + self.add_ui() + self.db.Commit() + + if hasattr(self.distribution, 'dist_files'): + tup = 'bdist_msi', self.target_version or 'any', fullname + self.distribution.dist_files.append(tup) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def add_files(self): + db = self.db + cab = msilib.CAB("distfiles") + rootdir = os.path.abspath(self.bdist_dir) + + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + f = Feature(db, "Python", "Python", "Everything", + 0, 1, directory="TARGETDIR") + + items = [(f, root, '')] + for version in self.versions + [self.other_version]: + target = "TARGETDIR" + version + name = default = "Python" + version + desc = "Everything" + if version is self.other_version: + title = "Python from another location" + level = 2 + else: + title = "Python %s from registry" % version + level = 1 + f = Feature(db, name, title, desc, 1, level, directory=target) + dir = Directory(db, cab, root, rootdir, target, default) + items.append((f, dir, version)) + db.Commit() + + seen = {} + for feature, dir, version in items: + todo = [dir] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + short = "%s|%s" % (dir.make_short(file), file) + default = file + version + newdir = Directory(db, cab, dir, file, default, short) + todo.append(newdir) + else: + if not dir.component: + dir.start_component(dir.logical, feature, 0) + if afile not in seen: + key = seen[afile] = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError( + "Multiple files with name %s" % file) + self.install_script_key = '[#%s]' % key + else: + key = seen[afile] + add_data(self.db, "DuplicateFile", + [(key + version, dir.component, key, None, dir.logical)]) + db.Commit() + cab.commit(db) + + def add_find_python(self): + """Adds code to the installer to compute the location of Python. + + Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the + registry for each version of Python. + + Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, + else from PYTHON.MACHINE.X.Y. + + Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" + + start = 402 + for ver in self.versions: + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver + machine_reg = "python.machine." + ver + user_reg = "python.user." + ver + machine_prop = "PYTHON.MACHINE." + ver + user_prop = "PYTHON.USER." + ver + machine_action = "PythonFromMachine" + ver + user_action = "PythonFromUser" + ver + exe_action = "PythonExe" + ver + target_dir_prop = "TARGETDIR" + ver + exe_prop = "PYTHON" + ver + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 + add_data(self.db, "RegLocator", + [(machine_reg, 2, install_path, None, Type), + (user_reg, 1, install_path, None, Type)]) + add_data(self.db, "AppSearch", + [(machine_prop, machine_reg), + (user_prop, user_reg)]) + add_data(self.db, "CustomAction", + [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), + (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), + (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), + ]) + add_data(self.db, "InstallExecuteSequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "InstallUISequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "Condition", + [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) + start += 4 + assert start < 500 + + def add_scripts(self): + if self.install_script: + start = 6800 + for ver in self.versions + [self.other_version]: + install_action = "install_script." + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "CustomAction", + [(install_action, 50, exe_prop, self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [(install_action, "&Python%s=3" % ver, start)]) + start += 1 + # XXX pre-install scripts are currently refused in finalize_options() + # but if this feature is completed, it will also need to add + # entries for each version as the above code does + if self.pre_install_script: + scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") + f = open(scriptfn, "w") + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + f.write(open(self.pre_install_script).read()) + f.close() + add_data(self.db, "Binary", + [("PreInstall", msilib.Binary(scriptfn)) + ]) + add_data(self.db, "CustomAction", + [("PreInstall", 2, "PreInstall", None) + ]) + add_data(self.db, "InstallExecuteSequence", + [("PreInstall", "NOT Installed", 450)]) + + + def add_ui(self): + db = self.db + x = y = 50 + w = 370 + h = 300 + title = "[ProductName] Setup" + + # see "Dialog Style Bits" + modal = 3 # visible | modal + modeless = 1 # visible + + # UI customization properties + add_data(db, "Property", + # See "DefaultUIFont Property" + [("DefaultUIFont", "DlgFont8"), + # See "ErrorDialog Style Bit" + ("ErrorDialog", "ErrorDlg"), + ("Progress1", "Install"), # modified in maintenance type dlg + ("Progress2", "installs"), + ("MaintenanceForm_Action", "Repair"), + # possible values: ALL, JUSTME + ("WhichUsers", "ALL") + ]) + + # Fonts, see "TextStyle Table" + add_data(db, "TextStyle", + [("DlgFont8", "Tahoma", 9, None, 0), + ("DlgFontBold8", "Tahoma", 8, None, 1), #bold + ("VerdanaBold10", "Verdana", 10, None, 1), + ("VerdanaRed9", "Verdana", 9, 255, 0), + ]) + + # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" + # Numbers indicate sequence; see sequence.py for how these action integrate + add_data(db, "InstallUISequence", + [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), + ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), + # In the user interface, assume all-users installation if privileged. + ("SelectFeaturesDlg", "Not Installed", 1230), + # XXX no support for resume installations yet + #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), + ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), + ("ProgressDlg", None, 1280)]) + + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) + ##################################################################### + # Standard dialogs: FatalError, UserExit, ExitDialog + fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + fatal.title("[ProductName] Installer ended prematurely") + fatal.back("< Back", "Finish", active = 0) + fatal.cancel("Cancel", "Back", active = 0) + fatal.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") + fatal.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c=fatal.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + user_exit.title("[ProductName] Installer was interrupted") + user_exit.back("< Back", "Finish", active = 0) + user_exit.cancel("Cancel", "Back", active = 0) + user_exit.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup was interrupted. Your system has not been modified. " + "To install this program at a later time, please run the installation again.") + user_exit.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = user_exit.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + exit_dialog.title("Completing the [ProductName] Installer") + exit_dialog.back("< Back", "Finish", active = 0) + exit_dialog.cancel("Cancel", "Back", active = 0) + exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = exit_dialog.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Return") + + ##################################################################### + # Required dialog: FilesInUse, ErrorDlg + inuse = PyDialog(db, "FilesInUse", + x, y, w, h, + 19, # KeepModeless|Modal|Visible + title, + "Retry", "Retry", "Retry", bitmap=False) + inuse.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Files in Use") + inuse.text("Description", 20, 23, 280, 20, 0x30003, + "Some files that need to be updated are currently in use.") + inuse.text("Text", 20, 55, 330, 50, 3, + "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") + inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", + None, None, None) + c=inuse.back("Exit", "Ignore", name="Exit") + c.event("EndDialog", "Exit") + c=inuse.next("Ignore", "Retry", name="Ignore") + c.event("EndDialog", "Ignore") + c=inuse.cancel("Retry", "Exit", name="Retry") + c.event("EndDialog","Retry") + + # See "Error Dialog". See "ICE20" for the required names of the controls. + error = Dialog(db, "ErrorDlg", + 50, 10, 330, 101, + 65543, # Error|Minimize|Modal|Visible + title, + "ErrorText", None, None) + error.text("ErrorText", 50,9,280,48,3, "") + #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) + error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") + error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") + error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") + error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") + error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") + error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") + error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") + + ##################################################################### + # Global "Query Cancel" dialog + cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, + "No", "No", "No") + cancel.text("Text", 48, 15, 194, 30, 3, + "Are you sure you want to cancel [ProductName] installation?") + #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, + # "py.ico", None, None) + c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") + c.event("EndDialog", "Exit") + + c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") + c.event("EndDialog", "Return") + + ##################################################################### + # Global "Wait for costing" dialog + costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, + "Return", "Return", "Return") + costing.text("Text", 48, 15, 194, 30, 3, + "Please wait while the installer finishes determining your disk space requirements.") + c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) + c.event("EndDialog", "Exit") + + ##################################################################### + # Preparation dialog: no user input except cancellation + prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel") + prep.text("Description", 15, 70, 320, 40, 0x30003, + "Please wait while the Installer prepares to guide you through the installation.") + prep.title("Welcome to the [ProductName] Installer") + c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") + c.mapping("ActionText", "Text") + c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) + c.mapping("ActionData", "Text") + prep.back("Back", None, active=0) + prep.next("Next", None, active=0) + c=prep.cancel("Cancel", None) + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Feature (Python directory) selection + seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + seldlg.title("Select Python Installations") + + seldlg.text("Hint", 15, 30, 300, 20, 3, + "Select the Python locations where %s should be installed." + % self.distribution.get_fullname()) + + seldlg.back("< Back", None, active=0) + c = seldlg.next("Next >", "Cancel") + order = 1 + c.event("[TARGETDIR]", "[SourceDir]", ordering=order) + for version in self.versions + [self.other_version]: + order += 1 + c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, + "FEATURE_SELECTED AND &Python%s=3" % version, + ordering=order) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) + c.event("EndDialog", "Return", ordering=order + 2) + c = seldlg.cancel("Cancel", "Features") + c.event("SpawnDialog", "CancelDlg") + + c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, + "FEATURE", None, "PathEdit", None) + c.event("[FEATURE_SELECTED]", "1") + ver = self.other_version + install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver + dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver + + c = seldlg.text("Other", 15, 200, 300, 15, 3, + "Provide an alternate Python location") + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, + "TARGETDIR" + ver, None, "Next", None) + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + ##################################################################### + # Disk cost + cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, + "OK", "OK", "OK", bitmap=False) + cost.text("Title", 15, 6, 200, 15, 0x30003, + "{\DlgFontBold8}Disk Space Requirements") + cost.text("Description", 20, 20, 280, 20, 0x30003, + "The disk space required for the installation of the selected features.") + cost.text("Text", 20, 53, 330, 60, 3, + "The highlighted volumes (if any) do not have enough disk space " + "available for the currently selected features. You can either " + "remove some files from the highlighted volumes, or choose to " + "install less features onto local drive(s), or select different " + "destination drive(s).") + cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, + None, "{120}{70}{70}{70}{70}", None, None) + cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") + + ##################################################################### + # WhichUsers Dialog. Only available on NT, and for privileged users. + # This must be run before FindRelatedProducts, because that will + # take into account whether the previous installation was per-user + # or per-machine. We currently don't support going back to this + # dialog after "Next" was selected; to support this, we would need to + # find how to reset the ALLUSERS property, and how to re-run + # FindRelatedProducts. + # On Windows9x, the ALLUSERS property is ignored on the command line + # and in the Property table, but installer fails according to the documentation + # if a dialog attempts to set ALLUSERS. + whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, + "AdminInstall", "Next", "Cancel") + whichusers.title("Select whether to install [ProductName] for all users of this computer.") + # A radio group with two options: allusers, justme + g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, + "WhichUsers", "", "Next") + g.add("ALL", 0, 5, 150, 20, "Install for all users") + g.add("JUSTME", 0, 25, 150, 20, "Install just for me") + + whichusers.back("Back", None, active=0) + + c = whichusers.next("Next >", "Cancel") + c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) + c.event("EndDialog", "Return", ordering = 2) + + c = whichusers.cancel("Cancel", "AdminInstall") + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Installation Progress dialog (modeless) + progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel", bitmap=False) + progress.text("Title", 20, 15, 200, 15, 0x30003, + "{\DlgFontBold8}[Progress1] [ProductName]") + progress.text("Text", 35, 65, 300, 30, 3, + "Please wait while the Installer [Progress2] [ProductName]. " + "This may take several minutes.") + progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") + + c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") + c.mapping("ActionText", "Text") + + #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) + #c.mapping("ActionData", "Text") + + c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, + None, "Progress done", None, None) + c.mapping("SetProgress", "Progress") + + progress.back("< Back", "Next", active=False) + progress.next("Next >", "Cancel", active=False) + progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") + + ################################################################### + # Maintenance type: repair/uninstall + maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + maint.title("Welcome to the [ProductName] Setup Wizard") + maint.text("BodyText", 15, 63, 330, 42, 3, + "Select whether you want to repair or remove [ProductName].") + g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, + "MaintenanceForm_Action", "", "Next") + #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") + g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") + g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") + + maint.back("< Back", None, active=False) + c=maint.next("Finish", "Cancel") + # Change installation: Change progress dialog to "Change", then ask + # for feature selection + #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) + #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) + + # Reinstall: Change progress dialog to "Repair", then invoke reinstall + # Also set list of reinstalled features to "ALL" + c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) + c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) + c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) + c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) + + # Uninstall: Change progress to "Remove", then invoke uninstall + # Also set list of removed features to "ALL" + c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) + c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) + c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) + c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) + + # Close dialog when maintenance action scheduled + c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) + #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) + + maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") + + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + if self.target_version: + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + else: + base_name = "%s.%s.msi" % (fullname, self.plat_name) + installer_name = os.path.join(self.dist_dir, base_name) + return installer_name diff --git a/python/lib/distutils/distutils/command/bdist_rpm.py b/python/lib/distutils/distutils/command/bdist_rpm.py new file mode 100644 index 00000000..59582436 --- /dev/null +++ b/python/lib/distutils/distutils/command/bdist_rpm.py @@ -0,0 +1,587 @@ +"""distutils.command.bdist_rpm + +Implements the Distutils 'bdist_rpm' command (create RPM source and binary +distributions).""" + +__revision__ = "$Id$" + +import sys +import os +import string + +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.file_util import write_file +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsFileError, DistutilsExecError) +from distutils import log + +class bdist_rpm (Command): + + description = "create an RPM distribution" + + user_options = [ + ('bdist-base=', None, + "base directory for creating built distributions"), + ('rpm-base=', None, + "base directory for creating RPMs (defaults to \"rpm\" under " + "--bdist-base; must be specified for RPM 2)"), + ('dist-dir=', 'd', + "directory to put final RPM files in " + "(and .spec files if --spec-only)"), + ('python=', None, + "path to Python interpreter to hard-code in the .spec file " + "(default: \"python\")"), + ('fix-python', None, + "hard-code the exact path to the current Python interpreter in " + "the .spec file"), + ('spec-only', None, + "only regenerate spec file"), + ('source-only', None, + "only generate source RPM"), + ('binary-only', None, + "only generate binary RPM"), + ('use-bzip2', None, + "use bzip2 instead of gzip to create source distribution"), + + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ('distribution-name=', None, + "name of the (Linux) distribution to which this " + "RPM applies (*not* the name of the module distribution!)"), + ('group=', None, + "package classification [default: \"Development/Libraries\"]"), + ('release=', None, + "RPM release number"), + ('serial=', None, + "RPM serial number"), + ('vendor=', None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]"), + ('packager=', None, + "RPM packager (eg. \"Jane Doe \")" + "[default: vendor]"), + ('doc-files=', None, + "list of documentation files (space or comma-separated)"), + ('changelog=', None, + "RPM changelog"), + ('icon=', None, + "name of icon file"), + ('provides=', None, + "capabilities provided by this package"), + ('requires=', None, + "capabilities required by this package"), + ('conflicts=', None, + "capabilities which conflict with this package"), + ('build-requires=', None, + "capabilities required to build this package"), + ('obsoletes=', None, + "capabilities made obsolete by this package"), + ('no-autoreq', None, + "do not automatically calculate dependencies"), + + # Actions to take when building RPM + ('keep-temp', 'k', + "don't clean up RPM build directory"), + ('no-keep-temp', None, + "clean up RPM build directory [default]"), + ('use-rpm-opt-flags', None, + "compile with RPM_OPT_FLAGS when building from source RPM"), + ('no-rpm-opt-flags', None, + "do not pass any RPM CFLAGS to compiler"), + ('rpm3-mode', None, + "RPM 3 compatibility mode (default)"), + ('rpm2-mode', None, + "RPM 2 compatibility mode"), + + # Add the hooks necessary for specifying custom scripts + ('prep-script=', None, + "Specify a script for the PREP phase of RPM building"), + ('build-script=', None, + "Specify a script for the BUILD phase of RPM building"), + + ('pre-install=', None, + "Specify a script for the pre-INSTALL phase of RPM building"), + ('install-script=', None, + "Specify a script for the INSTALL phase of RPM building"), + ('post-install=', None, + "Specify a script for the post-INSTALL phase of RPM building"), + + ('pre-uninstall=', None, + "Specify a script for the pre-UNINSTALL phase of RPM building"), + ('post-uninstall=', None, + "Specify a script for the post-UNINSTALL phase of RPM building"), + + ('clean-script=', None, + "Specify a script for the CLEAN phase of RPM building"), + + ('verify-script=', None, + "Specify a script for the VERIFY phase of the RPM build"), + + # Allow a packager to explicitly force an architecture + ('force-arch=', None, + "Force an architecture onto the RPM build process"), + + ('quiet', 'q', + "Run the INSTALL phase of RPM building in quiet mode"), + ] + + boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', + 'no-autoreq', 'quiet'] + + negative_opt = {'no-keep-temp': 'keep-temp', + 'no-rpm-opt-flags': 'use-rpm-opt-flags', + 'rpm2-mode': 'rpm3-mode'} + + + def initialize_options (self): + self.bdist_base = None + self.rpm_base = None + self.dist_dir = None + self.python = None + self.fix_python = None + self.spec_only = None + self.binary_only = None + self.source_only = None + self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_script = None + self.build_script = None + self.install_script = None + self.clean_script = None + self.verify_script = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + + self.keep_temp = 0 + self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 + self.no_autoreq = 0 + + self.force_arch = None + self.quiet = 0 + + # initialize_options() + + + def finalize_options (self): + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + if self.rpm_base is None: + if not self.rpm3_mode: + raise DistutilsOptionError, \ + "you must specify --rpm-base in RPM 2 mode" + self.rpm_base = os.path.join(self.bdist_base, "rpm") + + if self.python is None: + if self.fix_python: + self.python = sys.executable + else: + self.python = "python" + elif self.fix_python: + raise DistutilsOptionError, \ + "--python and --fix-python are mutually exclusive options" + + if os.name != 'posix': + raise DistutilsPlatformError, \ + ("don't know how to create RPM " + "distributions on platform %s" % os.name) + if self.binary_only and self.source_only: + raise DistutilsOptionError, \ + "cannot supply both '--source-only' and '--binary-only'" + + # don't pass CFLAGS to pure python distributions + if not self.distribution.has_ext_modules(): + self.use_rpm_opt_flags = 0 + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.finalize_package_data() + + # finalize_options() + + def finalize_package_data (self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string('vendor', + "%s <%s>" % (self.distribution.get_contact(), + self.distribution.get_contact_email())) + self.ensure_string('packager') + self.ensure_string_list('doc_files') + if isinstance(self.doc_files, list): + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc_files.append(readme) + + self.ensure_string('release', "1") + self.ensure_string('serial') # should it be an int? + + self.ensure_string('distribution_name') + + self.ensure_string('changelog') + # Format changelog correctly + self.changelog = self._format_changelog(self.changelog) + + self.ensure_filename('icon') + + self.ensure_filename('prep_script') + self.ensure_filename('build_script') + self.ensure_filename('install_script') + self.ensure_filename('clean_script') + self.ensure_filename('verify_script') + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + self.ensure_string('force_arch') + # finalize_package_data () + + + def run (self): + + if DEBUG: + print "before _get_package_data():" + print "vendor =", self.vendor + print "packager =", self.packager + print "doc_files =", self.doc_files + print "changelog =", self.changelog + + # make directories + if self.spec_only: + spec_dir = self.dist_dir + self.mkpath(spec_dir) + else: + rpm_dir = {} + for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): + rpm_dir[d] = os.path.join(self.rpm_base, d) + self.mkpath(rpm_dir[d]) + spec_dir = rpm_dir['SPECS'] + + # Spec file goes into 'dist_dir' if '--spec-only specified', + # build/rpm. otherwise. + spec_path = os.path.join(spec_dir, + "%s.spec" % self.distribution.get_name()) + self.execute(write_file, + (spec_path, + self._make_spec_file()), + "writing '%s'" % spec_path) + + if self.spec_only: # stop if requested + return + + # Make a source distribution and copy to SOURCES directory with + # optional icon. + saved_dist_files = self.distribution.dist_files[:] + sdist = self.reinitialize_command('sdist') + if self.use_bzip2: + sdist.formats = ['bztar'] + else: + sdist.formats = ['gztar'] + self.run_command('sdist') + self.distribution.dist_files = saved_dist_files + + source = sdist.get_archive_files()[0] + source_dir = rpm_dir['SOURCES'] + self.copy_file(source, source_dir) + + if self.icon: + if os.path.exists(self.icon): + self.copy_file(self.icon, source_dir) + else: + raise DistutilsFileError, \ + "icon file '%s' does not exist" % self.icon + + + # build package + log.info("building RPMs") + rpm_cmd = ['rpm'] + if os.path.exists('/usr/bin/rpmbuild') or \ + os.path.exists('/bin/rpmbuild'): + rpm_cmd = ['rpmbuild'] + + if self.source_only: # what kind of RPMs? + rpm_cmd.append('-bs') + elif self.binary_only: + rpm_cmd.append('-bb') + else: + rpm_cmd.append('-ba') + if self.rpm3_mode: + rpm_cmd.extend(['--define', + '_topdir %s' % os.path.abspath(self.rpm_base)]) + if not self.keep_temp: + rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + + rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + try: + binary_rpms = [] + source_rpm = None + while 1: + line = out.readline() + if not line: + break + l = string.split(string.strip(line)) + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() + + self.spawn(rpm_cmd) + + if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + + if not self.binary_only: + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + + if not self.source_only: + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, + os.path.basename(rpm)) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + # run() + + def _dist_path(self, path): + return os.path.join(self.dist_dir, os.path.basename(path)) + + def _make_spec_file(self): + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers + spec_file = [ + '%define name ' + self.distribution.get_name(), + '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), + '%define release ' + self.release.replace('-','_'), + '', + 'Summary: ' + self.distribution.get_description(), + ] + + # put locale summaries into spec file + # XXX not supported for now (hard to put a dictionary + # in a config file -- arg!) + #for locale in self.summaries.keys(): + # spec_file.append('Summary(%s): %s' % (locale, + # self.summaries[locale])) + + spec_file.extend([ + 'Name: %{name}', + 'Version: %{version}', + 'Release: %{release}',]) + + # XXX yuck! this filename is available from the "sdist" command, + # but only after it has run: and we create the spec file before + # running "sdist", in case of --spec-only. + if self.use_bzip2: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') + else: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') + + spec_file.extend([ + 'License: ' + self.distribution.get_license(), + 'Group: ' + self.group, + 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', + 'Prefix: %{_prefix}', ]) + + if not self.force_arch: + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArch: noarch') + else: + spec_file.append( 'BuildArch: %s' % self.force_arch ) + + for field in ('Vendor', + 'Packager', + 'Provides', + 'Requires', + 'Conflicts', + 'Obsoletes', + ): + val = getattr(self, string.lower(field)) + if isinstance(val, list): + spec_file.append('%s: %s' % (field, string.join(val))) + elif val is not None: + spec_file.append('%s: %s' % (field, val)) + + + if self.distribution.get_url() != 'UNKNOWN': + spec_file.append('Url: ' + self.distribution.get_url()) + + if self.distribution_name: + spec_file.append('Distribution: ' + self.distribution_name) + + if self.build_requires: + spec_file.append('BuildRequires: ' + + string.join(self.build_requires)) + + if self.icon: + spec_file.append('Icon: ' + os.path.basename(self.icon)) + + if self.no_autoreq: + spec_file.append('AutoReq: 0') + + spec_file.extend([ + '', + '%description', + self.distribution.get_long_description() + ]) + + # put locale descriptions into spec file + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + #for locale in self.descriptions.keys(): + # spec_file.extend([ + # '', + # '%description -l ' + locale, + # self.descriptions[locale], + # ]) + + # rpm scripts + # figure out default build script + def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) + def_build = "%s build" % def_setup_call + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build + + # insert contents of files + + # XXX this is kind of misleading: user-supplied options are files + # that we open and interpolate into the spec file, but the defaults + # are just text that we drop in as-is. Hmmm. + + install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % def_setup_call + + script_options = [ + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), + ('build', 'build_script', def_build), + ('install', 'install_script', install_cmd), + ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('verifyscript', 'verify_script', None), + ('pre', 'pre_install', None), + ('post', 'post_install', None), + ('preun', 'pre_uninstall', None), + ('postun', 'post_uninstall', None), + ] + + for (rpm_opt, attr, default) in script_options: + # Insert contents of file referred to, if no file is referred to + # use 'default' as contents of script + val = getattr(self, attr) + if val or default: + spec_file.extend([ + '', + '%' + rpm_opt,]) + if val: + spec_file.extend(string.split(open(val, 'r').read(), '\n')) + else: + spec_file.append(default) + + + # files section + spec_file.extend([ + '', + '%files -f INSTALLED_FILES', + '%defattr(-,root,root)', + ]) + + if self.doc_files: + spec_file.append('%doc ' + string.join(self.doc_files)) + + if self.changelog: + spec_file.extend([ + '', + '%changelog',]) + spec_file.extend(self.changelog) + + return spec_file + + # _make_spec_file () + + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings + """ + if not changelog: + return changelog + new_changelog = [] + for line in string.split(string.strip(changelog), '\n'): + line = string.strip(line) + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog + + # _format_changelog() + +# class bdist_rpm diff --git a/python/lib/distutils/distutils/command/bdist_wininst.py b/python/lib/distutils/distutils/command/bdist_wininst.py new file mode 100644 index 00000000..aa9383af --- /dev/null +++ b/python/lib/distutils/distutils/command/bdist_wininst.py @@ -0,0 +1,368 @@ +"""distutils.command.bdist_wininst + +Implements the Distutils 'bdist_wininst' command: create a windows installer +exe-program.""" + +__revision__ = "$Id$" + +import sys +import os +import string + +from sysconfig import get_python_version + +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils import log +from distutils.util import get_platform + +class bdist_wininst (Command): + + description = "create an executable installer for MS Windows" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized)" + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('bitmap=', 'b', + "bitmap to use for the installer instead of python-powered logo"), + ('title=', 't', + "title to display on the installer background instead of default"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after" + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ('user-access-control=', None, + "specify Vista's UAC handling - 'none'/default=no " + "handling, 'auto'=use UAC if target Python installed for " + "all users, 'force'=always use UAC"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + def initialize_options (self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.bitmap = None + self.title = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.user_access_control = None + + # initialize_options() + + + def finalize_options (self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + if self.skip_build and self.plat_name: + # If build is skipped and plat_name is overridden, bdist will + # not see the correct 'plat_name' - so set that up manually. + bdist = self.distribution.get_command_obj('bdist') + bdist.plat_name = self.plat_name + # next the command will be initialized using that name + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'wininst') + + if not self.target_version: + self.target_version = "" + + if not self.skip_build and self.distribution.has_ext_modules(): + short_version = get_python_version() + if self.target_version and self.target_version != short_version: + raise DistutilsOptionError, \ + "target version can only be %s, or the '--skip-build'" \ + " option must be specified" % (short_version,) + self.target_version = short_version + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError, \ + "install_script '%s' not found in scripts" % \ + self.install_script + # finalize_options() + + + def run (self): + if (sys.platform != "win32" and + (self.distribution.has_ext_modules() or + self.distribution.has_c_libraries())): + raise DistutilsPlatformError \ + ("distribution contains extensions and/or C libraries; " + "must be compiled on a Windows 32 platform") + + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + install.plat_name = self.plat_name + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = sys.version[0:3] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + # Use a custom scheme for the zip-file, because we have to decide + # at installation time which scheme to use. + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + value = string.upper(key) + if key == 'headers': + value = value + '/Include/$dist_name' + setattr(install, + 'install_' + key, + value) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + # And make an archive relative to the root of the + # pseudo-installation tree. + from tempfile import mktemp + archive_basename = mktemp() + fullname = self.distribution.get_fullname() + arcname = self.make_archive(archive_basename, "zip", + root_dir=self.bdist_dir) + # create an exe containing the zip-file + self.create_exe(arcname, fullname, self.bitmap) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_wininst', pyversion, + self.get_installer_filename(fullname))) + # remove the zip-file again + log.debug("removing temporary file '%s'", arcname) + os.remove(arcname) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + # run() + + def get_inidata (self): + # Return data describing the installation. + + lines = [] + metadata = self.distribution.metadata + + # Write the [metadata] section. + lines.append("[metadata]") + + # 'info' will be displayed in the installer's dialog box, + # describing the items to be installed. + info = (metadata.long_description or '') + '\n' + + # Escape newline characters + def escape(s): + return string.replace(s, "\n", "\\n") + + for name in ["author", "author_email", "description", "maintainer", + "maintainer_email", "name", "url", "version"]: + data = getattr(metadata, name, "") + if data: + info = info + ("\n %s: %s" % \ + (string.capitalize(name), escape(data))) + lines.append("%s=%s" % (name, escape(data))) + + # The [setup] section contains entries controlling + # the installer runtime. + lines.append("\n[Setup]") + if self.install_script: + lines.append("install_script=%s" % self.install_script) + lines.append("info=%s" % escape(info)) + lines.append("target_compile=%d" % (not self.no_target_compile)) + lines.append("target_optimize=%d" % (not self.no_target_optimize)) + if self.target_version: + lines.append("target_version=%s" % self.target_version) + if self.user_access_control: + lines.append("user_access_control=%s" % self.user_access_control) + + title = self.title or self.distribution.get_fullname() + lines.append("title=%s" % escape(title)) + import time + import distutils + build_info = "Built %s with distutils-%s" % \ + (time.ctime(time.time()), distutils.__version__) + lines.append("build_info=%s" % build_info) + return string.join(lines, "\n") + + # get_inidata() + + def create_exe (self, arcname, fullname, bitmap=None): + import struct + + self.mkpath(self.dist_dir) + + cfgdata = self.get_inidata() + + installer_name = self.get_installer_filename(fullname) + self.announce("creating %s" % installer_name) + + if bitmap: + bitmapdata = open(bitmap, "rb").read() + bitmaplen = len(bitmapdata) + else: + bitmaplen = 0 + + file = open(installer_name, "wb") + file.write(self.get_exe_bytes()) + if bitmap: + file.write(bitmapdata) + + # Convert cfgdata from unicode to ascii, mbcs encoded + try: + unicode + except NameError: + pass + else: + if isinstance(cfgdata, unicode): + cfgdata = cfgdata.encode("mbcs") + + # Append the pre-install script + cfgdata = cfgdata + "\0" + if self.pre_install_script: + script_data = open(self.pre_install_script, "r").read() + cfgdata = cfgdata + script_data + "\n\0" + else: + # empty pre-install script + cfgdata = cfgdata + "\0" + file.write(cfgdata) + + # The 'magic number' 0x1234567B is used to make sure that the + # binary layout of 'cfgdata' is what the wininst.exe binary + # expects. If the layout changes, increment that number, make + # the corresponding changes to the wininst.exe sources, and + # recompile them. + header = struct.pack(" cur_version: + bv = get_build_version() + else: + if self.target_version < "2.4": + bv = 6.0 + else: + bv = 7.1 + else: + # for current version - use authoritative check. + bv = get_build_version() + + # wininst-x.y.exe is in the same directory as this file + directory = os.path.dirname(__file__) + # we must use a wininst-x.y.exe built with the same C compiler + # used for python. XXX What about mingw, borland, and so on? + + # if plat_name starts with "win" but is not "win32" + # we want to strip "win" and leave the rest (e.g. -amd64) + # for all other cases, we don't want any suffix + if self.plat_name != 'win32' and self.plat_name[:3] == 'win': + sfix = self.plat_name[3:] + else: + sfix = '' + + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) + f = open(filename, "rb") + try: + return f.read() + finally: + f.close() +# class bdist_wininst diff --git a/python/lib/distutils/distutils/command/build.py b/python/lib/distutils/distutils/command/build.py new file mode 100644 index 00000000..f84bf359 --- /dev/null +++ b/python/lib/distutils/distutils/command/build.py @@ -0,0 +1,147 @@ +"""distutils.command.build + +Implements the Distutils 'build' command.""" + +__revision__ = "$Id$" + +import sys, os + +from distutils.util import get_platform +from distutils.core import Command +from distutils.errors import DistutilsOptionError + +def show_compilers(): + from distutils.ccompiler import show_compilers + show_compilers() + +class build(Command): + + description = "build everything needed to install" + + user_options = [ + ('build-base=', 'b', + "base directory for build library"), + ('build-purelib=', None, + "build directory for platform-neutral distributions"), + ('build-platlib=', None, + "build directory for platform-specific distributions"), + ('build-lib=', None, + "build directory for all distribution (defaults to either " + + "build-purelib or build-platlib"), + ('build-scripts=', None, + "build directory for scripts"), + ('build-temp=', 't', + "temporary build directory"), + ('plat-name=', 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform()), + ('compiler=', 'c', + "specify the compiler type"), + ('debug', 'g', + "compile extensions and libraries with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('executable=', 'e', + "specify final destination interpreter path (build.py)"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_base = 'build' + # these are decided only after 'build_base' has its final value + # (unless overridden by the user or client) + self.build_purelib = None + self.build_platlib = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.compiler = None + self.plat_name = None + self.debug = None + self.force = 0 + self.executable = None + + def finalize_options(self): + if self.plat_name is None: + self.plat_name = get_platform() + else: + # plat-name only supported for windows (other platforms are + # supported via ./configure flags, if at all). Avoid misleading + # other platforms. + if os.name != 'nt': + raise DistutilsOptionError( + "--plat-name only supported on Windows (try " + "using './configure --help' on your platform)") + + plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3]) + + # Make it so Python 2.x and Python 2.x with --with-pydebug don't + # share the same build directories. Doing so confuses the build + # process for C modules + if hasattr(sys, 'gettotalrefcount'): + plat_specifier += '-pydebug' + + # 'build_purelib' and 'build_platlib' just default to 'lib' and + # 'lib.' under the base build directory. We only use one of + # them for a given distribution, though -- + if self.build_purelib is None: + self.build_purelib = os.path.join(self.build_base, 'lib') + if self.build_platlib is None: + self.build_platlib = os.path.join(self.build_base, + 'lib' + plat_specifier) + + # 'build_lib' is the actual directory that we will use for this + # particular module distribution -- if user didn't supply it, pick + # one of 'build_purelib' or 'build_platlib'. + if self.build_lib is None: + if self.distribution.ext_modules: + self.build_lib = self.build_platlib + else: + self.build_lib = self.build_purelib + + # 'build_temp' -- temporary directory for compiler turds, + # "build/temp." + if self.build_temp is None: + self.build_temp = os.path.join(self.build_base, + 'temp' + plat_specifier) + if self.build_scripts is None: + self.build_scripts = os.path.join(self.build_base, + 'scripts-' + sys.version[0:3]) + + if self.executable is None: + self.executable = os.path.normpath(sys.executable) + + def run(self): + # Run all relevant sub-commands. This will be some subset of: + # - build_py - pure Python modules + # - build_clib - standalone C libraries + # - build_ext - Python extensions + # - build_scripts - (Python) scripts + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # -- Predicates for the sub-command list --------------------------- + + def has_pure_modules (self): + return self.distribution.has_pure_modules() + + def has_c_libraries (self): + return self.distribution.has_c_libraries() + + def has_ext_modules (self): + return self.distribution.has_ext_modules() + + def has_scripts (self): + return self.distribution.has_scripts() + + sub_commands = [('build_py', has_pure_modules), + ('build_clib', has_c_libraries), + ('build_ext', has_ext_modules), + ('build_scripts', has_scripts), + ] diff --git a/python/lib/distutils/distutils/command/build_clib.py b/python/lib/distutils/distutils/command/build_clib.py new file mode 100644 index 00000000..205587e7 --- /dev/null +++ b/python/lib/distutils/distutils/command/build_clib.py @@ -0,0 +1,209 @@ +"""distutils.command.build_clib + +Implements the Distutils 'build_clib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + +__revision__ = "$Id$" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os +from distutils.core import Command +from distutils.errors import DistutilsSetupError +from distutils.sysconfig import customize_compiler +from distutils import log + +def show_compilers(): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_clib(Command): + + description = "build C/C++ libraries used by Python extensions" + + user_options = [ + ('build-clib=', 'b', + "directory to build C/C++ libraries to"), + ('build-temp=', 't', + "directory to put temporary build by-products"), + ('debug', 'g', + "compile with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_clib = None + self.build_temp = None + + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + self.debug = None + self.force = 0 + self.compiler = None + + + def finalize_options(self): + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. + self.set_undefined_options('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force')) + + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list(self.libraries) + + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + def run(self): + if not self.libraries: + return + + # Yech -- this is cut 'n pasted from build_ext.py! + from distutils.ccompiler import new_compiler + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + + self.build_libraries(self.libraries) + + + def check_library_list(self, libraries): + """Ensure that the list of libraries is valid. + + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(libraries, list): + raise DistutilsSetupError, \ + "'libraries' option must be a list of tuples" + + for lib in libraries: + if not isinstance(lib, tuple) and len(lib) != 2: + raise DistutilsSetupError, \ + "each element of 'libraries' must a 2-tuple" + + name, build_info = lib + + if not isinstance(name, str): + raise DistutilsSetupError, \ + "first element of each tuple in 'libraries' " + \ + "must be a string (the library name)" + if '/' in name or (os.sep != '/' and os.sep in name): + raise DistutilsSetupError, \ + ("bad library name '%s': " + + "may not contain directory separators") % \ + lib[0] + + if not isinstance(build_info, dict): + raise DistutilsSetupError, \ + "second element of each tuple in 'libraries' " + \ + "must be a dictionary (build info)" + + def get_library_names(self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + if not self.libraries: + return None + + lib_names = [] + for (lib_name, build_info) in self.libraries: + lib_names.append(lib_name) + return lib_names + + + def get_source_files(self): + self.check_library_list(self.libraries) + filenames = [] + for (lib_name, build_info) in self.libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError, \ + ("in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % lib_name + + filenames.extend(sources) + return filenames + + def build_libraries(self, libraries): + for (lib_name, build_info) in libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError, \ + ("in 'libraries' option (library '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % lib_name + sources = list(sources) + + log.info("building '%s' library", lib_name) + + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + debug=self.debug) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.create_static_lib(objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) diff --git a/python/lib/distutils/distutils/command/build_ext.py b/python/lib/distutils/distutils/command/build_ext.py new file mode 100644 index 00000000..f0a7d4ca --- /dev/null +++ b/python/lib/distutils/distutils/command/build_ext.py @@ -0,0 +1,766 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accommodate C++ +extensions ASAP).""" + +# This module should be kept compatible with Python 2.1. + +__revision__ = "$Id$" + +import sys, os, string, re +from types import * +from site import USER_BASE, USER_SITE +from distutils.core import Command +from distutils.errors import * +from distutils.sysconfig import customize_compiler, get_python_version +from distutils.dep_util import newer_group +from distutils.extension import Extension +from distutils.util import get_platform +from distutils import log + +if os.name == 'nt': + from distutils.msvccompiler import get_build_version + MSVC_VERSION = int(get_build_version()) + +# An extension name is just a dot-separated list of Python NAMEs (ie. +# the same as a fully-qualified module name). +extension_name_re = re.compile \ + (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') + + +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_ext (Command): + + description = "build C/C++ extensions (compile/link to build directory)" + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to finalize_options() (i.e. the constructor + # takes care of both command-line and client options + # in between initialize_options() and finalize_options()) + + sep_by = " (separated by '%s')" % os.pathsep + user_options = [ + ('build-lib=', 'b', + "directory for compiled extension modules"), + ('build-temp=', 't', + "directory for temporary files (build by-products)"), + ('plat-name=', 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform()), + ('inplace', 'i', + "ignore build-lib and put compiled extensions into the source " + + "directory alongside your pure Python modules"), + ('include-dirs=', 'I', + "list of directories to search for header files" + sep_by), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries" + sep_by), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ('debug', 'g', + "compile/link with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ('swig-cpp', None, + "make SWIG create C++ files (default is C)"), + ('swig-opts=', None, + "list of SWIG command line options"), + ('swig=', None, + "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath"), + ] + + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options (self): + self.extensions = None + self.build_lib = None + self.plat_name = None + self.build_temp = None + self.inplace = 0 + self.package = None + + self.include_dirs = None + self.define = None + self.undef = None + self.libraries = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + self.debug = None + self.force = None + self.compiler = None + self.swig = None + self.swig_cpp = None + self.swig_opts = None + self.user = None + + def finalize_options(self): + from distutils import sysconfig + + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force'), + ('plat_name', 'plat_name'), + ) + + if self.package is None: + self.package = self.distribution.ext_package + + self.extensions = self.distribution.ext_modules + + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # Put the Python "system" include dir at the end, so that + # any local include dirs take precedence. + self.include_dirs.append(py_include) + if plat_py_include != py_include: + self.include_dirs.append(plat_py_include) + + self.ensure_string_list('libraries') + + # Life is easier if we're not forever checking for None, so + # simplify these options to empty lists if unset + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + elif type(self.library_dirs) is StringType: + self.library_dirs = string.split(self.library_dirs, os.pathsep) + + if self.rpath is None: + self.rpath = [] + elif type(self.rpath) is StringType: + self.rpath = string.split(self.rpath, os.pathsep) + + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs + if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. + self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) + if self.debug: + self.build_temp = os.path.join(self.build_temp, "Debug") + else: + self.build_temp = os.path.join(self.build_temp, "Release") + + # Append the source distribution include and library directories, + # this allows distutils on windows to work in the source tree + self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) + if MSVC_VERSION == 9: + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = '' + else: + # win-amd64 or win-ia64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + + elif MSVC_VERSION == 8: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PC', 'VS8.0')) + elif MSVC_VERSION == 7: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PC', 'VS7.1')) + else: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PC', 'VC6')) + + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the + # import libraries in its "Config" subdirectory + if os.name == 'os2': + self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) + + # for extensions under Cygwin and AtheOS Python's library directory must be + # appended to library_dirs + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + # building third party extensions + self.library_dirs.append(os.path.join(sys.prefix, "lib", + "python" + get_python_version(), + "config")) + else: + # building python standard extensions + self.library_dirs.append('.') + + # For building extensions with a shared Python library, + # Python's library directory must be appended to library_dirs + # See Issues: #1600860, #4366 + if (sysconfig.get_config_var('Py_ENABLE_SHARED')): + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols + # specified by the 'define' option will be set to '1'. Multiple + # symbols can be separated with commas. + + if self.define: + defines = self.define.split(',') + self.define = map(lambda symbol: (symbol, '1'), defines) + + # The option for macros to undefine is also a string from the + # option parsing, but has to be a list. Multiple symbols can also + # be separated with commas here. + if self.undef: + self.undef = self.undef.split(',') + + if self.swig_opts is None: + self.swig_opts = [] + else: + self.swig_opts = self.swig_opts.split(' ') + + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + + def run(self): + from distutils.ccompiler import new_compiler + + # 'self.extensions', as supplied by setup.py, is a list of + # Extension instances. See the documentation for Extension (in + # distutils.extension) for details. + # + # For backwards compatibility with Distutils 0.8.2 and earlier, we + # also allow the 'extensions' list to be a list of tuples: + # (ext_name, build_info) + # where build_info is a dictionary containing everything that + # Extension instances do except the name, with a few things being + # differently named. We convert these 2-tuples to Extension + # instances as needed. + + if not self.extensions: + return + + # If we were asked to build any C/C++ libraries, make sure that the + # directory where we put them is in the library search path for + # linking extensions. + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.libraries.extend(build_clib.get_library_names() or []) + self.library_dirs.append(build_clib.build_clib) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) + + # And make sure that any compile/link-related options (which might + # come from the command-line or from the setup script) are set in + # that CCompiler object -- that way, they automatically apply to + # all compiling and linking done here. + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name, value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + if self.libraries is not None: + self.compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + self.compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects(self.link_objects) + + # Now actually compile and link everything. + self.build_extensions() + + def check_extensions_list(self, extensions): + """Ensure that the list of extensions (presumably provided as a + command option 'extensions') is valid, i.e. it is a list of + Extension objects. We also support the old-style list of 2-tuples, + where the tuples are (ext_name, build_info), which are converted to + Extension instances here. + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(extensions, list): + raise DistutilsSetupError, \ + "'ext_modules' option must be a list of Extension instances" + + for i, ext in enumerate(extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + + if not isinstance(ext, tuple) or len(ext) != 2: + raise DistutilsSetupError, \ + ("each element of 'ext_modules' option must be an " + "Extension instance or 2-tuple") + + ext_name, build_info = ext + + log.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) + + if not (isinstance(ext_name, str) and + extension_name_re.match(ext_name)): + raise DistutilsSetupError, \ + ("first element of each tuple in 'ext_modules' " + "must be the extension name (a string)") + + if not isinstance(build_info, dict): + raise DistutilsSetupError, \ + ("second element of each tuple in 'ext_modules' " + "must be a dictionary (build info)") + + # OK, the (ext_name, build_info) dict is type-safe: convert it + # to an Extension instance. + ext = Extension(ext_name, build_info['sources']) + + # Easy stuff: one-to-one mapping from dict elements to + # instance attributes. + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', + 'extra_link_args'): + val = build_info.get(key) + if val is not None: + setattr(ext, key, val) + + # Medium-easy stuff: same syntax/semantics, different names. + ext.runtime_library_dirs = build_info.get('rpath') + if 'def_file' in build_info: + log.warn("'def_file' element of build info dict " + "no longer supported") + + # Non-trivial stuff: 'macros' split into 'define_macros' + # and 'undef_macros'. + macros = build_info.get('macros') + if macros: + ext.define_macros = [] + ext.undef_macros = [] + for macro in macros: + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): + raise DistutilsSetupError, \ + ("'macros' element of build info dict " + "must be 1- or 2-tuple") + if len(macro) == 1: + ext.undef_macros.append(macro[0]) + elif len(macro) == 2: + ext.define_macros.append(macro) + + extensions[i] = ext + + def get_source_files(self): + self.check_extensions_list(self.extensions) + filenames = [] + + # Wouldn't it be neat if we knew the names of header files too... + for ext in self.extensions: + filenames.extend(ext.sources) + + return filenames + + def get_outputs(self): + # Sanity check the 'extensions' list -- can't assume this is being + # done in the same run as a 'build_extensions()' call (in fact, we + # can probably assume that it *isn't*!). + self.check_extensions_list(self.extensions) + + # And build the list of output (built) filenames. Note that this + # ignores the 'inplace' flag, and assumes everything goes in the + # "build" tree. + outputs = [] + for ext in self.extensions: + outputs.append(self.get_ext_fullpath(ext.name)) + return outputs + + def build_extensions(self): + # First, sanity-check the 'extensions' list + self.check_extensions_list(self.extensions) + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + sources = ext.sources + if sources is None or type(sources) not in (ListType, TupleType): + raise DistutilsSetupError, \ + ("in 'ext_modules' option (extension '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % ext.name + sources = list(sources) + + ext_path = self.get_ext_fullpath(ext.name) + depends = sources + ext.depends + if not (self.force or newer_group(depends, ext_path, 'newer')): + log.debug("skipping '%s' extension (up-to-date)", ext.name) + return + else: + log.info("building '%s' extension", ext.name) + + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources, ext) + + # Next, compile the source code to object files. + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accommodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precedence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args or [] + + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) + + # XXX -- this is a Vile HACK! + # + # The setup.py script for Python on Unix needs to be able to + # get this list so it can perform all the clean up needed to + # avoid keeping object files around when cleaning out a failed + # build of an extension module. Since Distutils does not + # track dependencies, we have to get rid of intermediates to + # ensure all the intermediates will be properly re-built. + # + self._built_objects = objects[:] + + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. + if ext.extra_objects: + objects.extend(ext.extra_objects) + extra_args = ext.extra_link_args or [] + + # Detect target language, if not provided + language = ext.language or self.compiler.detect_language(sources) + + self.compiler.link_shared_object( + objects, ext_path, + libraries=self.get_libraries(ext), + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), + debug=self.debug, + build_temp=self.build_temp, + target_lang=language) + + + def swig_sources (self, sources, extension): + + """Walk the list of source files in 'sources', looking for SWIG + interface (.i) files. Run SWIG on all that are found, and + return a modified 'sources' list with SWIG source files replaced + by the generated C (or C++) files. + """ + + new_sources = [] + swig_sources = [] + swig_targets = {} + + # XXX this drops generated C/C++ files into the source tree, which + # is fine for developers who want to distribute the generated + # source -- but there should be an option to put SWIG output in + # the temp dir. + + if self.swig_cpp: + log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): + target_ext = '.cpp' + else: + target_ext = '.c' + + for source in sources: + (base, ext) = os.path.splitext(source) + if ext == ".i": # SWIG interface file + new_sources.append(base + '_wrap' + target_ext) + swig_sources.append(source) + swig_targets[source] = new_sources[-1] + else: + new_sources.append(source) + + if not swig_sources: + return new_sources + + swig = self.swig or self.find_swig() + swig_cmd = [swig, "-python"] + swig_cmd.extend(self.swig_opts) + if self.swig_cpp: + swig_cmd.append("-c++") + + # Do not override commandline arguments + if not self.swig_opts: + for o in extension.swig_opts: + swig_cmd.append(o) + + for source in swig_sources: + target = swig_targets[source] + log.info("swigging %s to %s", source, target) + self.spawn(swig_cmd + ["-o", target, source]) + + return new_sources + + # swig_sources () + + def find_swig (self): + """Return the name of the SWIG executable. On Unix, this is + just "swig" -- it should be in the PATH. Tries a bit harder on + Windows. + """ + + if os.name == "posix": + return "swig" + elif os.name == "nt": + + # Look for SWIG in its standard installation directory on + # Windows (or so I presume!). If we find it there, great; + # if not, act like Unix and assume it's in the PATH. + for vers in ("1.3", "1.2", "1.1"): + fn = os.path.join("c:\\swig%s" % vers, "swig.exe") + if os.path.isfile(fn): + return fn + else: + return "swig.exe" + + elif os.name == "os2": + # assume swig available in the PATH. + return "swig.exe" + + else: + raise DistutilsPlatformError, \ + ("I don't know how to find (much less run) SWIG " + "on platform '%s'") % os.name + + # find_swig () + + # -- Name generators ----------------------------------------------- + # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + # makes sure the extension name is only using dots + all_dots = string.maketrans('/'+os.sep, '..') + ext_name = ext_name.translate(all_dots) + + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + filename = self.get_ext_filename(ext_name) + filename = os.path.split(filename)[-1] + + if not self.inplace: + # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) + return os.path.join(self.build_lib, filename) + + # the inplace option requires to find the package directory + # using the build_py command for that + package = '.'.join(modpath[0:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename + return os.path.join(package_dir, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. + + Adds the `package.` prefix""" + if self.package is None: + return ext_name + else: + return self.package + '.' + ext_name + + def get_ext_filename(self, ext_name): + r"""Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + from distutils.sysconfig import get_config_var + ext_path = string.split(ext_name, '.') + # OS/2 has an 8 character module (extension) limit :-( + if os.name == "os2": + ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] + # extensions in debug_mode are named 'module_d.pyd' under windows + so_ext = get_config_var('SO') + if os.name == 'nt' and self.debug: + return os.path.join(*ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + so_ext + + def get_export_symbols (self, ext): + """Return the list of symbols that a shared extension has to + export. This either uses 'ext.export_symbols' or, if it's not + provided, "init" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "init" function. + """ + initfunc_name = "init" + ext.name.split('.')[-1] + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) + return ext.export_symbols + + def get_libraries (self, ext): + """Return the list of libraries to link against when building a + shared extension. On most platforms, this is just 'ext.libraries'; + on Windows and OS/2, we add the Python library (eg. python20.dll). + """ + # The python library is always needed on Windows. For MSVC, this + # is redundant, since the library is mentioned in a pragma in + # pyconfig.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + # Append '_d' to the python import library on debug builds. + if sys.platform == "win32": + from distutils.msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + else: + return ext.libraries + elif sys.platform == "os2emx": + # EMX/GCC requires the python library explicitly, and I + # believe VACPP does as well (though not confirmed) - AIM Apr01 + template = "python%d%d" + # debug versions of the main DLL aren't supported, at least + # not at this time - AIM Apr01 + #if self.debug: + # template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + elif sys.platform[:6] == "cygwin": + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + elif sys.platform[:6] == "atheos": + from distutils import sysconfig + + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # Get SHLIBS from Makefile + extra = [] + for lib in sysconfig.get_config_var('SHLIBS').split(): + if lib.startswith('-l'): + extra.append(lib[2:]) + else: + extra.append(lib) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib, "m"] + extra + + elif sys.platform == 'darwin': + # Don't use the default code below + return ext.libraries + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries + else: + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + return ext.libraries + [pythonlib] + else: + return ext.libraries + +# class build_ext diff --git a/python/lib/distutils/distutils/command/build_py.py b/python/lib/distutils/distutils/command/build_py.py new file mode 100644 index 00000000..04c455f0 --- /dev/null +++ b/python/lib/distutils/distutils/command/build_py.py @@ -0,0 +1,393 @@ +"""distutils.command.build_py + +Implements the Distutils 'build_py' command.""" + +__revision__ = "$Id$" + +import os +import sys +from glob import glob + +from distutils.core import Command +from distutils.errors import DistutilsOptionError, DistutilsFileError +from distutils.util import convert_path +from distutils import log + +class build_py(Command): + + description = "\"build\" pure Python modules (copy to build directory)" + + user_options = [ + ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('compile', 'c', "compile .py to .pyc"), + ('no-compile', None, "don't compile .py files [default]"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ] + + boolean_options = ['compile', 'force'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + self.build_lib = None + self.py_modules = None + self.package = None + self.package_data = None + self.package_dir = None + self.compile = 0 + self.optimize = 0 + self.force = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('force', 'force')) + + # Get the distribution options that are aliases for build_py + # options -- list of packages and list of modules. + self.packages = self.distribution.packages + self.py_modules = self.distribution.py_modules + self.package_data = self.distribution.package_data + self.package_dir = {} + if self.distribution.package_dir: + for name, path in self.distribution.package_dir.items(): + self.package_dir[name] = convert_path(path) + self.data_files = self.get_data_files() + + # Ick, copied straight from install_lib.py (fancy_getopt needs a + # type system! Hell, *everything* needs a type system!!!) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # XXX copy_file by default preserves atime and mtime. IMHO this is + # the right thing to do, but perhaps it should be an option -- in + # particular, a site administrator might want installed files to + # reflect the time of installation rather than the last + # modification time before the installed release. + + # XXX copy_file by default preserves mode, which appears to be the + # wrong thing to do: if a file is read-only in the working + # directory, we want it to be installed read/write so that the next + # installation of the same module distribution can overwrite it + # without problems. (This might be a Unix-specific issue.) Thus + # we turn off 'preserve_mode' when copying to the build directory, + # since the build directory is supposed to be exactly what the + # installation will look like (ie. we preserve mode when + # installing). + + # Two options control which modules will be installed: 'packages' + # and 'py_modules'. The former lets us work with whole packages, not + # specifying individual modules at all; the latter is for + # specifying modules one-at-a-time. + + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + data = [] + if not self.packages: + return data + for package in self.packages: + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Length of path to strip from found files + plen = 0 + if src_dir: + plen = len(src_dir)+1 + + # Strip directory from globbed filenames + filenames = [ + file[plen:] for file in self.find_data_files(package, src_dir) + ] + data.append((package, src_dir, build_dir, filenames)) + return data + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = [] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + filelist = glob(os.path.join(src_dir, convert_path(pattern))) + # Files that match more than one pattern are only added once + files.extend([fn for fn in filelist if fn not in files]) + return files + + def build_package_data(self): + """Copy data files into build directory""" + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + self.mkpath(os.path.dirname(target)) + self.copy_file(os.path.join(src_dir, filename), target, + preserve_mode=False) + + def get_package_dir(self, package): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" + + path = package.split('.') + + if not self.package_dir: + if path: + return os.path.join(*path) + else: + return '' + else: + tail = [] + while path: + try: + pdir = self.package_dir['.'.join(path)] + except KeyError: + tail.insert(0, path[-1]) + del path[-1] + else: + tail.insert(0, pdir) + return os.path.join(*tail) + else: + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = self.package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + + if tail: + return os.path.join(*tail) + else: + return '' + + def check_package(self, package, package_dir): + # Empty dir name means current directory, which we can probably + # assume exists. Also, os.path.exists and isdir don't know about + # my "empty string means current dir" convention, so we have to + # circumvent them. + if package_dir != "": + if not os.path.exists(package_dir): + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir) + if not os.path.isdir(package_dir): + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir) + + # Require __init__.py for all but the "root package" + if package: + init_py = os.path.join(package_dir, "__init__.py") + if os.path.isfile(init_py): + return init_py + else: + log.warn(("package init file '%s' not found " + + "(or not a regular file)"), init_py) + + # Either not in a package at all (__init__.py not expected), or + # __init__.py doesn't exist -- so don't return the filename. + return None + + def check_module(self, module, module_file): + if not os.path.isfile(module_file): + log.warn("file %s (for module %s) not found", module_file, module) + return False + else: + return True + + def find_package_modules(self, package, package_dir): + self.check_package(package, package_dir) + module_files = glob(os.path.join(package_dir, "*.py")) + modules = [] + setup_script = os.path.abspath(self.distribution.script_name) + + for f in module_files: + abs_f = os.path.abspath(f) + if abs_f != setup_script: + module = os.path.splitext(os.path.basename(f))[0] + modules.append((package, module, f)) + else: + self.debug_print("excluding %s" % setup_script) + return modules + + def find_modules(self): + """Finds individually-specified Python modules, ie. those listed by + module name in 'self.py_modules'. Returns a list of tuples (package, + module_base, filename): 'package' is a tuple of the path through + package-space to the module; 'module_base' is the bare (no + packages, no dots) module name, and 'filename' is the path to the + ".py" file (relative to the distribution root) that implements the + module. + """ + # Map package names to tuples of useful info about the package: + # (package_dir, checked) + # package_dir - the directory where we'll find source files for + # this package + # checked - true if we have checked that the package directory + # is valid (exists, contains __init__.py, ... ?) + packages = {} + + # List of (package, module, filename) tuples to return + modules = [] + + # We treat modules-in-packages almost the same as toplevel modules, + # just the "package" for a toplevel is empty (either an empty + # string or empty list, depending on context). Differences: + # - don't check for __init__.py in directory for empty package + for module in self.py_modules: + path = module.split('.') + package = '.'.join(path[0:-1]) + module_base = path[-1] + + try: + (package_dir, checked) = packages[package] + except KeyError: + package_dir = self.get_package_dir(package) + checked = 0 + + if not checked: + init_py = self.check_package(package, package_dir) + packages[package] = (package_dir, 1) + if init_py: + modules.append((package, "__init__", init_py)) + + # XXX perhaps we should also check for just .pyc files + # (so greedy closed-source bastards can distribute Python + # modules too) + module_file = os.path.join(package_dir, module_base + ".py") + if not self.check_module(module, module_file): + continue + + modules.append((package, module_base, module_file)) + + return modules + + def find_all_modules(self): + """Compute the list of all modules that will be built, whether + they are specified one-module-at-a-time ('self.py_modules') or + by whole packages ('self.packages'). Return a list of tuples + (package, module, module_file), just like 'find_modules()' and + 'find_package_modules()' do.""" + modules = [] + if self.py_modules: + modules.extend(self.find_modules()) + if self.packages: + for package in self.packages: + package_dir = self.get_package_dir(package) + m = self.find_package_modules(package, package_dir) + modules.extend(m) + return modules + + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] + + def get_module_outfile(self, build_dir, package, module): + outfile_path = [build_dir] + list(package) + [module + ".py"] + return os.path.join(*outfile_path) + + def get_outputs(self, include_bytecode=1): + modules = self.find_all_modules() + outputs = [] + for (package, module, module_file) in modules: + package = package.split('.') + filename = self.get_module_outfile(self.build_lib, package, module) + outputs.append(filename) + if include_bytecode: + if self.compile: + outputs.append(filename + "c") + if self.optimize > 0: + outputs.append(filename + "o") + + outputs += [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + + return outputs + + def build_module(self, module, module_file, package): + if isinstance(package, str): + package = package.split('.') + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple") + + # Now put the module source file into the "build" area -- this is + # easy, we just copy it somewhere under self.build_lib (the build + # directory for Python source). + outfile = self.get_module_outfile(self.build_lib, package, module) + dir = os.path.dirname(outfile) + self.mkpath(dir) + return self.copy_file(module_file, outfile, preserve_mode=0) + + def build_modules(self): + modules = self.find_modules() + for (package, module, module_file) in modules: + + # Now "build" the module -- ie. copy the source file to + # self.build_lib (the build directory for Python source). + # (Actually, it gets copied to the directory for this package + # under self.build_lib.) + self.build_module(module, module_file, package) + + def build_packages(self): + for package in self.packages: + + # Get list of (package, module, module_file) tuples based on + # scanning the package directory. 'package' is only included + # in the tuple so that 'find_modules()' and + # 'find_package_tuples()' have a consistent interface; it's + # ignored here (apart from a sanity check). Also, 'module' is + # the *unqualified* module name (ie. no dots, no package -- we + # already know its package!), and 'module_file' is the path to + # the .py file, relative to the current directory + # (ie. including 'package_dir'). + package_dir = self.get_package_dir(package) + modules = self.find_package_modules(package, package_dir) + + # Now loop over the modules we found, "building" each one (just + # copy it to self.build_lib). + for (package_, module, module_file) in modules: + assert package == package_ + self.build_module(module, module_file, package) + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + prefix = self.build_lib + if prefix[-1] != os.sep: + prefix = prefix + os.sep + + # XXX this code is essentially the same as the 'byte_compile() + # method of the "install_lib" command, except for the determination + # of the 'prefix' string. Hmmm. + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=prefix, dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=prefix, dry_run=self.dry_run) diff --git a/python/lib/distutils/distutils/command/build_scripts.py b/python/lib/distutils/distutils/command/build_scripts.py new file mode 100644 index 00000000..567df658 --- /dev/null +++ b/python/lib/distutils/distutils/command/build_scripts.py @@ -0,0 +1,131 @@ +"""distutils.command.build_scripts + +Implements the Distutils 'build_scripts' command.""" + +__revision__ = "$Id$" + +import os, re +from stat import ST_MODE +from distutils.core import Command +from distutils.dep_util import newer +from distutils.util import convert_path +from distutils import log + +# check if Python is called on the first line with this expression +first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') + +class build_scripts (Command): + + description = "\"build\" scripts (copy and fixup #! line)" + + user_options = [ + ('build-dir=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('executable=', 'e', "specify final destination interpreter path"), + ] + + boolean_options = ['force'] + + + def initialize_options (self): + self.build_dir = None + self.scripts = None + self.force = None + self.executable = None + self.outfiles = None + + def finalize_options (self): + self.set_undefined_options('build', + ('build_scripts', 'build_dir'), + ('force', 'force'), + ('executable', 'executable')) + self.scripts = self.distribution.scripts + + def get_source_files(self): + return self.scripts + + def run (self): + if not self.scripts: + return + self.copy_scripts() + + + def copy_scripts (self): + """Copy each script listed in 'self.scripts'; if it's marked as a + Python script in the Unix way (first line matches 'first_line_re', + ie. starts with "\#!" and contains "python"), then adjust the first + line to refer to the current Python interpreter as we copy. + """ + _sysconfig = __import__('sysconfig') + self.mkpath(self.build_dir) + outfiles = [] + for script in self.scripts: + adjust = 0 + script = convert_path(script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) + + if not self.force and not newer(script, outfile): + log.debug("not copying %s (up-to-date)", script) + continue + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, "r") + except IOError: + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) + continue + + match = first_line_re.match(first_line) + if match: + adjust = 1 + post_interp = match.group(1) or '' + + if adjust: + log.info("copying and adjusting %s -> %s", script, + self.build_dir) + if not self.dry_run: + outf = open(outfile, "w") + if not _sysconfig.is_python_build(): + outf.write("#!%s%s\n" % + (self.executable, + post_interp)) + else: + outf.write("#!%s%s\n" % + (os.path.join( + _sysconfig.get_config_var("BINDIR"), + "python%s%s" % (_sysconfig.get_config_var("VERSION"), + _sysconfig.get_config_var("EXE"))), + post_interp)) + outf.writelines(f.readlines()) + outf.close() + if f: + f.close() + else: + if f: + f.close() + self.copy_file(script, outfile) + + if os.name == 'posix': + for file in outfiles: + if self.dry_run: + log.info("changing mode of %s", file) + else: + oldmode = os.stat(file)[ST_MODE] & 07777 + newmode = (oldmode | 0555) & 07777 + if newmode != oldmode: + log.info("changing mode of %s from %o to %o", + file, oldmode, newmode) + os.chmod(file, newmode) + + # copy_scripts () + +# class build_scripts diff --git a/python/lib/distutils/distutils/command/check.py b/python/lib/distutils/distutils/command/check.py new file mode 100644 index 00000000..152bf0de --- /dev/null +++ b/python/lib/distutils/distutils/command/check.py @@ -0,0 +1,149 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" +__revision__ = "$Id$" + +from distutils.core import Command +from distutils.dist import PKG_INFO_ENCODING +from distutils.errors import DistutilsSetupError + +try: + # docutils is installed + from docutils.utils import Reporter + from docutils.parsers.rst import Parser + from docutils import frontend + from docutils import nodes + from StringIO import StringIO + + class SilentReporter(Reporter): + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding='ascii', error_handler='replace'): + self.messages = [] + Reporter.__init__(self, source, report_level, halt_level, stream, + debug, encoding, error_handler) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + return nodes.system_message(message, level=level, + type=self.levels[level], + *children, **kwargs) + + HAS_DOCUTILS = True +except ImportError: + # docutils is not installed + HAS_DOCUTILS = False + +class check(Command): + """This command checks the meta-data of the package. + """ + description = ("perform some checks on the package") + user_options = [('metadata', 'm', 'Verify meta-data'), + ('restructuredtext', 'r', + ('Checks if long string meta-data syntax ' + 'are reStructuredText-compliant')), + ('strict', 's', + 'Will exit with an error if a check fails')] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if HAS_DOCUTILS: + self.check_restructuredtext() + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 0: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + name, version, URL, (author and author_email) or + (maintainer and maintainer_email)). + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ', '.join(missing)) + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + if not isinstance(data, unicode): + data = data.decode(PKG_INFO_ENCODING) + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = '%s (line %s)' % (warning[1], line) + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + source_path = StringIO() + parser = Parser() + settings = frontend.OptionParser().get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter(source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError: + reporter.messages.append((-1, 'Could not finish the parsing.', + '', {})) + + return reporter.messages diff --git a/python/lib/distutils/distutils/command/clean.py b/python/lib/distutils/distutils/command/clean.py new file mode 100644 index 00000000..90ef35f1 --- /dev/null +++ b/python/lib/distutils/distutils/command/clean.py @@ -0,0 +1,80 @@ +"""distutils.command.clean + +Implements the Distutils 'clean' command.""" + +# contributed by Bastian Kleineidam , added 2000-03-18 + +__revision__ = "$Id$" + +import os +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils import log + +class clean(Command): + + description = "clean up temporary files from 'build' command" + user_options = [ + ('build-base=', 'b', + "base build directory (default: 'build.build-base')"), + ('build-lib=', None, + "build directory for all modules (default: 'build.build-lib')"), + ('build-temp=', 't', + "temporary build directory (default: 'build.build-temp')"), + ('build-scripts=', None, + "build directory for scripts (default: 'build.build-scripts')"), + ('bdist-base=', None, + "temporary directory for built distributions"), + ('all', 'a', + "remove all build output, not just temporary by-products") + ] + + boolean_options = ['all'] + + def initialize_options(self): + self.build_base = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.bdist_base = None + self.all = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_scripts', 'build_scripts'), + ('build_temp', 'build_temp')) + self.set_undefined_options('bdist', + ('bdist_base', 'bdist_base')) + + def run(self): + # remove the build/temp. directory (unless it's already + # gone) + if os.path.exists(self.build_temp): + remove_tree(self.build_temp, dry_run=self.dry_run) + else: + log.debug("'%s' does not exist -- can't clean it", + self.build_temp) + + if self.all: + # remove build directories + for directory in (self.build_lib, + self.bdist_base, + self.build_scripts): + if os.path.exists(directory): + remove_tree(directory, dry_run=self.dry_run) + else: + log.warn("'%s' does not exist -- can't clean it", + directory) + + # just for the heck of it, try to remove the base build directory: + # we might have emptied it right now, but if not we don't care + if not self.dry_run: + try: + os.rmdir(self.build_base) + log.info("removing '%s'", self.build_base) + except OSError: + pass + +# class clean diff --git a/python/lib/distutils/distutils/command/config.py b/python/lib/distutils/distutils/command/config.py new file mode 100644 index 00000000..b0849135 --- /dev/null +++ b/python/lib/distutils/distutils/command/config.py @@ -0,0 +1,357 @@ +"""distutils.command.config + +Implements the Distutils 'config' command, a (mostly) empty command class +that exists mainly to be sub-classed by specific module distributions and +applications. The idea is that while every "config" command is different, +at least they're all named the same, and users always see "config" in the +list of standard commands. Also, this is a good place to put common +configure-like tasks: "try to compile this C code", or "figure out where +this header file lives". +""" + +__revision__ = "$Id$" + +import os +import re + +from distutils.core import Command +from distutils.errors import DistutilsExecError +from distutils.sysconfig import customize_compiler +from distutils import log + +LANG_EXT = {'c': '.c', 'c++': '.cxx'} + +class config(Command): + + description = "prepare to build" + + user_options = [ + ('compiler=', None, + "specify the compiler type"), + ('cc=', None, + "specify the compiler executable"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + + ('noisy', None, + "show every action (compile, link, run, ...) taken"), + ('dump-source', None, + "dump generated source files before attempting to compile them"), + ] + + + # The three standard command methods: since the "config" command + # does nothing by default, these are empty. + + def initialize_options(self): + self.compiler = None + self.cc = None + self.include_dirs = None + self.libraries = None + self.library_dirs = None + + # maximal output for now + self.noisy = 1 + self.dump_source = 1 + + # list of temporary files generated along-the-way that we have + # to clean at some point + self.temp_files = [] + + def finalize_options(self): + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + elif isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + if self.libraries is None: + self.libraries = [] + elif isinstance(self.libraries, str): + self.libraries = [self.libraries] + + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + def run(self): + pass + + + # Utility methods for actual "config" commands. The interfaces are + # loosely based on Autoconf macros of similar names. Sub-classes + # may use these freely. + + def _check_compiler(self): + """Check that 'self.compiler' really is a CCompiler object; + if not, make it one. + """ + # We do this late, and only on-demand, because this is an expensive + # import. + from distutils.ccompiler import CCompiler, new_compiler + if not isinstance(self.compiler, CCompiler): + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, force=1) + customize_compiler(self.compiler) + if self.include_dirs: + self.compiler.set_include_dirs(self.include_dirs) + if self.libraries: + self.compiler.set_libraries(self.libraries) + if self.library_dirs: + self.compiler.set_library_dirs(self.library_dirs) + + + def _gen_temp_sourcefile(self, body, headers, lang): + filename = "_configtest" + LANG_EXT[lang] + file = open(filename, "w") + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") + file.close() + return filename + + def _preprocess(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + out = "_configtest.i" + self.temp_files.extend([src, out]) + self.compiler.preprocess(src, out, include_dirs=include_dirs) + return (src, out) + + def _compile(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + if self.dump_source: + dump_file(src, "compiling '%s':" % src) + (obj,) = self.compiler.object_filenames([src]) + self.temp_files.extend([src, obj]) + self.compiler.compile([src], include_dirs=include_dirs) + return (src, obj) + + def _link(self, body, headers, include_dirs, libraries, library_dirs, + lang): + (src, obj) = self._compile(body, headers, include_dirs, lang) + prog = os.path.splitext(os.path.basename(src))[0] + self.compiler.link_executable([obj], prog, + libraries=libraries, + library_dirs=library_dirs, + target_lang=lang) + + if self.compiler.exe_extension is not None: + prog = prog + self.compiler.exe_extension + self.temp_files.append(prog) + + return (src, obj, prog) + + def _clean(self, *filenames): + if not filenames: + filenames = self.temp_files + self.temp_files = [] + log.info("removing: %s", ' '.join(filenames)) + for filename in filenames: + try: + os.remove(filename) + except OSError: + pass + + + # XXX these ignore the dry-run flag: what to do, what to do? even if + # you want a dry-run build, you still need some sort of configuration + # info. My inclination is to make it up to the real config command to + # consult 'dry_run', and assume a default (minimal) configuration if + # true. The problem with trying to do it here is that you'd have to + # return either true or false from all the 'try' methods, neither of + # which is correct. + + # XXX need access to the header search path and maybe default macros. + + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): + """Construct a source file from 'body' (a string containing lines + of C/C++ code) and 'headers' (a list of header files to include) + and run it through the preprocessor. Return true if the + preprocessor succeeded, false if there were any errors. + ('body' probably isn't of much use, but what the heck.) + """ + from distutils.ccompiler import CompileError + self._check_compiler() + ok = 1 + try: + self._preprocess(body, headers, include_dirs, lang) + except CompileError: + ok = 0 + + self._clean() + return ok + + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, + lang="c"): + """Construct a source file (just like 'try_cpp()'), run it through + the preprocessor, and return true if any line of the output matches + 'pattern'. 'pattern' should either be a compiled regex object or a + string containing a regex. If both 'body' and 'headers' are None, + preprocesses an empty file -- which can be useful to determine the + symbols the preprocessor and compiler set by default. + """ + self._check_compiler() + src, out = self._preprocess(body, headers, include_dirs, lang) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + file = open(out) + match = 0 + while 1: + line = file.readline() + if line == '': + break + if pattern.search(line): + match = 1 + break + + file.close() + self._clean() + return match + + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): + """Try to compile a source file built from 'body' and 'headers'. + Return true on success, false otherwise. + """ + from distutils.ccompiler import CompileError + self._check_compiler() + try: + self._compile(body, headers, include_dirs, lang) + ok = 1 + except CompileError: + ok = 0 + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_link(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile and link a source file, built from 'body' and + 'headers', to executable form. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + ok = 1 + except (CompileError, LinkError): + ok = 0 + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_run(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile, link to an executable, and run a program + built from 'body' and 'headers'. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + src, obj, exe = self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + self.spawn([exe]) + ok = 1 + except (CompileError, LinkError, DistutilsExecError): + ok = 0 + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + + # -- High-level methods -------------------------------------------- + # (these are the ones that are actually likely to be useful + # when implementing a real-world config command!) + + def check_func(self, func, headers=None, include_dirs=None, + libraries=None, library_dirs=None, decl=0, call=0): + + """Determine if function 'func' is available by constructing a + source file that refers to 'func', and compiles and links it. + If everything succeeds, returns true; otherwise returns false. + + The constructed source file starts out by including the header + files listed in 'headers'. If 'decl' is true, it then declares + 'func' (as "int func()"); you probably shouldn't supply 'headers' + and set 'decl' true in the same call, or you might get errors about + a conflicting declarations for 'func'. Finally, the constructed + 'main()' function either references 'func' or (if 'call' is true) + calls it. 'libraries' and 'library_dirs' are used when + linking. + """ + + self._check_compiler() + body = [] + if decl: + body.append("int %s ();" % func) + body.append("int main () {") + if call: + body.append(" %s();" % func) + else: + body.append(" %s;" % func) + body.append("}") + body = "\n".join(body) + "\n" + + return self.try_link(body, headers, include_dirs, + libraries, library_dirs) + + # check_func () + + def check_lib(self, library, library_dirs=None, headers=None, + include_dirs=None, other_libraries=[]): + """Determine if 'library' is available to be linked against, + without actually checking that any particular symbols are provided + by it. 'headers' will be used in constructing the source file to + be compiled, but the only effect of this is to check if all the + header files listed are available. Any libraries listed in + 'other_libraries' will be included in the link, in case 'library' + has symbols that depend on other libraries. + """ + self._check_compiler() + return self.try_link("int main (void) { }", + headers, include_dirs, + [library]+other_libraries, library_dirs) + + def check_header(self, header, include_dirs=None, library_dirs=None, + lang="c"): + """Determine if the system header file named by 'header_file' + exists and can be found by the preprocessor; return true if so, + false otherwise. + """ + return self.try_cpp(body="/* No body */", headers=[header], + include_dirs=include_dirs) + + +def dump_file(filename, head=None): + """Dumps a file content into log.info. + + If head is not None, will be dumped before the file content. + """ + if head is None: + log.info('%s' % filename) + else: + log.info(head) + file = open(filename) + try: + log.info(file.read()) + finally: + file.close() diff --git a/python/lib/distutils/distutils/command/install.py b/python/lib/distutils/distutils/command/install.py new file mode 100644 index 00000000..b9f1c6c5 --- /dev/null +++ b/python/lib/distutils/distutils/command/install.py @@ -0,0 +1,672 @@ +"""distutils.command.install + +Implements the Distutils 'install' command.""" + +from distutils import log + +# This module should be kept compatible with Python 2.1. + +__revision__ = "$Id$" + +import sys, os, string +from types import * +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.sysconfig import get_config_vars +from distutils.errors import DistutilsPlatformError +from distutils.file_util import write_file +from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform +from distutils.errors import DistutilsOptionError +from site import USER_BASE +from site import USER_SITE + + +if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'headers': '$base/include/python$py_version_short/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/lib/python', + 'headers': '$base/include/python/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + 'nt': WINDOWS_SCHEME, + 'nt_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + }, + 'os2': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'os2_home': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + } + +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + + +class install (Command): + + description = "install everything from build directory" + + user_options = [ + # Select installation scheme and set base director(y|ies) + ('prefix=', None, + "installation prefix"), + ('exec-prefix=', None, + "(Unix only) prefix for platform-specific files"), + ('home=', None, + "(Unix only) home directory to install under"), + ('user', None, + "install in user site-package '%s'" % USER_SITE), + + # Or, just set the base director(y|ies) + ('install-base=', None, + "base installation directory (instead of --prefix or --home)"), + ('install-platbase=', None, + "base installation directory for platform-specific files " + + "(instead of --exec-prefix or --home)"), + ('root=', None, + "install everything relative to this alternate root directory"), + + # Or, explicitly set the installation scheme + ('install-purelib=', None, + "installation directory for pure Python module distributions"), + ('install-platlib=', None, + "installation directory for non-pure module distributions"), + ('install-lib=', None, + "installation directory for all module distributions " + + "(overrides --install-purelib and --install-platlib)"), + + ('install-headers=', None, + "installation directory for C/C++ headers"), + ('install-scripts=', None, + "installation directory for Python scripts"), + ('install-data=', None, + "installation directory for data files"), + + # Byte-compilation options -- see install_lib.py for details, as + # these are duplicated from there (but only install_lib does + # anything with them). + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + + # Miscellaneous control options + ('force', 'f', + "force installation (overwrite any existing files)"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + + # Where to install documentation (eventually!) + #('doc-format=', None, "format of documentation to generate"), + #('install-man=', None, "directory for Unix man pages"), + #('install-html=', None, "directory for HTML documentation"), + #('install-info=', None, "directory for GNU info files"), + + ('record=', None, + "filename in which to record list of installed files"), + ] + + boolean_options = ['compile', 'force', 'skip-build', 'user'] + negative_opt = {'no-compile' : 'compile'} + + + def initialize_options (self): + + # High-level options: these select both an installation base + # and scheme. + self.prefix = None + self.exec_prefix = None + self.home = None + self.user = 0 + + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying + # the --install-{platlib,purelib,scripts,data} options). + self.install_base = None + self.install_platbase = None + self.root = None + + # These options are the actual installation directories; if not + # supplied by the user, they are filled in using the installation + # scheme implied by prefix/exec-prefix/home and the contents of + # that installation scheme. + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib + self.install_scripts = None + self.install_data = None + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE + + self.compile = None + self.optimize = None + + # These two are for putting non-packagized distributions into their + # own directory and creating a .pth file if it makes sense. + # 'extra_path' comes from the setup file; 'install_path_file' can + # be turned off if it makes no sense to install a .pth file. (But + # better to install it uselessly than to guess wrong and not + # install it when it's necessary and would be used!) Currently, + # 'install_path_file' is always true unless some outsider meddles + # with it. + self.extra_path = None + self.install_path_file = 1 + + # 'force' forces installation, even if target files are not + # out-of-date. 'skip_build' skips running the "build" command, + # handy if you know it's not necessary. 'warn_dir' (which is *not* + # a user option, it's just there so the bdist_* commands can turn + # it off) determines whether we warn about installing to a + # directory not in sys.path. + self.force = 0 + self.skip_build = 0 + self.warn_dir = 1 + + # These are only here as a conduit from the 'build' command to the + # 'install_*' commands that do the real work. ('build_base' isn't + # actually used anywhere, but it might be useful in future.) They + # are not user options, because if the user told the install + # command where the build directory is, that wouldn't affect the + # build command. + self.build_base = None + self.build_lib = None + + # Not defined yet because we don't know anything about + # documentation yet. + #self.install_man = None + #self.install_html = None + #self.install_info = None + + self.record = None + + + # -- Option finalizing methods ------------------------------------- + # (This is rather more involved than for most commands, + # because this is where the policy for installing third- + # party Python modules on various platforms given a wide + # array of user input is decided. Yes, it's quite complex!) + + def finalize_options (self): + + # This method (and its pliant slaves, like 'finalize_unix()', + # 'finalize_other()', and 'select_scheme()') is where the default + # installation directories for modules, extension modules, and + # anything else we care to install from a Python module + # distribution. Thus, this code makes a pretty important policy + # statement about how third-party stuff is added to a Python + # installation! Note that the actual work of installation is done + # by the relatively simple 'install_*' commands; they just take + # their orders from the installation directory options determined + # here. + + # Check for errors/inconsistencies in the options; first, stuff + # that's wrong on any platform. + + if ((self.prefix or self.exec_prefix or self.home) and + (self.install_base or self.install_platbase)): + raise DistutilsOptionError, \ + ("must supply either prefix/exec-prefix/home or " + + "install-base/install-platbase -- not both") + + if self.home and (self.prefix or self.exec_prefix): + raise DistutilsOptionError, \ + "must supply either home or prefix/exec-prefix -- not both" + + if self.user and (self.prefix or self.exec_prefix or self.home or + self.install_base or self.install_platbase): + raise DistutilsOptionError("can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base") + + # Next, stuff that's wrong (or dubious) only on certain platforms. + if os.name != "posix": + if self.exec_prefix: + self.warn("exec-prefix option ignored on this platform") + self.exec_prefix = None + + # Now the interesting logic -- so interesting that we farm it out + # to other methods. The goal of these methods is to set the final + # values for the install_{lib,scripts,data,...} options, using as + # input a heady brew of prefix, exec_prefix, home, install_base, + # install_platbase, user-supplied versions of + # install_{purelib,platlib,lib,scripts,data,...}, and the + # INSTALL_SCHEME dictionary above. Phew! + + self.dump_dirs("pre-finalize_{unix,other}") + + if os.name == 'posix': + self.finalize_unix() + else: + self.finalize_other() + + self.dump_dirs("post-finalize_{unix,other}()") + + # Expand configuration variables, tilde, etc. in self.install_base + # and self.install_platbase -- that way, we can use $base or + # $platbase in the other installation directories and not worry + # about needing recursive variable expansion (shudder). + + py_version = (string.split(sys.version))[0] + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + self.config_vars = {'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[0:3], + 'py_version_nodot': py_version[0] + py_version[2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'userbase': self.install_userbase, + 'usersite': self.install_usersite, + } + self.expand_basedirs() + + self.dump_dirs("post-expand_basedirs()") + + # Now define config vars for the base directories so we can expand + # everything else. + self.config_vars['base'] = self.install_base + self.config_vars['platbase'] = self.install_platbase + + if DEBUG: + from pprint import pprint + print "config vars:" + pprint(self.config_vars) + + # Expand "~" and configuration variables in the installation + # directories. + self.expand_dirs() + + self.dump_dirs("post-expand_dirs()") + + # Create directories in the home dir: + if self.user: + self.create_home_path() + + # Pick the actual directory to install all modules to: either + # install_purelib or install_platlib, depending on whether this + # module distribution is pure or not. Of course, if the user + # already specified install_lib, use their selection. + if self.install_lib is None: + if self.distribution.ext_modules: # has extensions: non-pure + self.install_lib = self.install_platlib + else: + self.install_lib = self.install_purelib + + + # Convert directories from Unix /-separated syntax to the local + # convention. + self.convert_paths('lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers', + 'userbase', 'usersite') + + # Well, we're not actually fully completely finalized yet: we still + # have to deal with 'extra_path', which is the hack for allowing + # non-packagized module distributions (hello, Numerical Python!) to + # get their own directories. + self.handle_extra_path() + self.install_libbase = self.install_lib # needed for .pth file + self.install_lib = os.path.join(self.install_lib, self.extra_dirs) + + # If a new root directory was supplied, make all the installation + # dirs relative to it. + if self.root is not None: + self.change_roots('libbase', 'lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') + + self.dump_dirs("after prepending root") + + # Find out the build directories, ie. where to install from. + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib')) + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + + # finalize_options () + + + def dump_dirs (self, msg): + if DEBUG: + from distutils.fancy_getopt import longopt_xlate + print msg + ":" + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = string.translate(self.negative_opt[opt_name], + longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = string.translate(opt_name, longopt_xlate) + val = getattr(self, opt_name) + print " %s: %s" % (opt_name, val) + + + def finalize_unix (self): + + if self.install_base is not None or self.install_platbase is not None: + if ((self.install_lib is None and + self.install_purelib is None and + self.install_platlib is None) or + self.install_headers is None or + self.install_scripts is None or + self.install_data is None): + raise DistutilsOptionError, \ + ("install-base or install-platbase supplied, but " + "installation scheme is incomplete") + return + + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("unix_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError, \ + "must not supply exec-prefix without prefix" + + self.prefix = os.path.normpath(sys.prefix) + self.exec_prefix = os.path.normpath(sys.exec_prefix) + + else: + if self.exec_prefix is None: + self.exec_prefix = self.prefix + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix + self.select_scheme("unix_prefix") + + # finalize_unix () + + + def finalize_other (self): # Windows and Mac OS for now + + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + self.prefix = os.path.normpath(sys.prefix) + + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme(os.name) + except KeyError: + raise DistutilsPlatformError, \ + "I don't know how to install stuff on '%s'" % os.name + + # finalize_other () + + + def select_scheme (self, name): + # it's the caller's problem if they supply a bad name! + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + + + def _expand_attrs (self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name == 'posix' or os.name == 'nt': + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + + def expand_basedirs (self): + self._expand_attrs(['install_base', + 'install_platbase', + 'root']) + + def expand_dirs (self): + self._expand_attrs(['install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data',]) + + + def convert_paths (self, *names): + for name in names: + attr = "install_" + name + setattr(self, attr, convert_path(getattr(self, attr))) + + + def handle_extra_path (self): + + if self.extra_path is None: + self.extra_path = self.distribution.extra_path + + if self.extra_path is not None: + if type(self.extra_path) is StringType: + self.extra_path = string.split(self.extra_path, ',') + + if len(self.extra_path) == 1: + path_file = extra_dirs = self.extra_path[0] + elif len(self.extra_path) == 2: + (path_file, extra_dirs) = self.extra_path + else: + raise DistutilsOptionError, \ + ("'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") + + # convert to local form in case Unix notation used (as it + # should be in setup scripts) + extra_dirs = convert_path(extra_dirs) + + else: + path_file = None + extra_dirs = '' + + # XXX should we warn if path_file and not extra_dirs? (in which + # case the path file would be harmless but pointless) + self.path_file = path_file + self.extra_dirs = extra_dirs + + # handle_extra_path () + + + def change_roots (self, *names): + for name in names: + attr = "install_" + name + setattr(self, attr, change_root(self.root, getattr(self, attr))) + + def create_home_path(self): + """Create directories under ~ + """ + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in self.config_vars.iteritems(): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0700)" % path) + os.makedirs(path, 0700) + + # -- Command execution methods ------------------------------------- + + def run (self): + + # Obviously have to build before we can install + if not self.skip_build: + self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when " + "cross-compiling") + + # Run all sub-commands (at least those that need to be run) + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.path_file: + self.create_path_file() + + # write list of installed files, if requested. + if self.record: + outputs = self.get_outputs() + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in xrange(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + self.execute(write_file, + (self.record, outputs), + "writing list of installed files to '%s'" % + self.record) + + sys_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normcase, sys_path) + install_lib = os.path.normcase(os.path.normpath(self.install_lib)) + if (self.warn_dir and + not (self.path_file and self.install_path_file) and + install_lib not in sys_path): + log.debug(("modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself"), + self.install_lib) + + # run () + + def create_path_file (self): + filename = os.path.join(self.install_libbase, + self.path_file + ".pth") + if self.install_path_file: + self.execute(write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) + else: + self.warn("path file '%s' not created" % filename) + + + # -- Reporting methods --------------------------------------------- + + def get_outputs (self): + # Assemble the outputs of all the sub-commands. + outputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + # Add the contents of cmd.get_outputs(), ensuring + # that outputs doesn't contain duplicate entries + for filename in cmd.get_outputs(): + if filename not in outputs: + outputs.append(filename) + + if self.path_file and self.install_path_file: + outputs.append(os.path.join(self.install_libbase, + self.path_file + ".pth")) + + return outputs + + def get_inputs (self): + # XXX gee, this looks familiar ;-( + inputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + inputs.extend(cmd.get_inputs()) + + return inputs + + + # -- Predicates for sub-command list ------------------------------- + + def has_lib (self): + """Return true if the current distribution has any Python + modules to install.""" + return (self.distribution.has_pure_modules() or + self.distribution.has_ext_modules()) + + def has_headers (self): + return self.distribution.has_headers() + + def has_scripts (self): + return self.distribution.has_scripts() + + def has_data (self): + return self.distribution.has_data_files() + + + # 'sub_commands': a list of commands this command might have to run to + # get its work done. See cmd.py for more info. + sub_commands = [('install_lib', has_lib), + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), + ('install_egg_info', lambda self:True), + ] + +# class install diff --git a/python/lib/distutils/distutils/command/install_data.py b/python/lib/distutils/distutils/command/install_data.py new file mode 100644 index 00000000..ab40797b --- /dev/null +++ b/python/lib/distutils/distutils/command/install_data.py @@ -0,0 +1,81 @@ +"""distutils.command.install_data + +Implements the Distutils 'install_data' command, for installing +platform-independent data files.""" + +# contributed by Bastian Kleineidam + +__revision__ = "$Id$" + +import os +from distutils.core import Command +from distutils.util import change_root, convert_path + +class install_data(Command): + + description = "install data files" + + user_options = [ + ('install-dir=', 'd', + "base directory for installing data files " + "(default: installation base dir)"), + ('root=', None, + "install everything relative to this alternate root directory"), + ('force', 'f', "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.outfiles = [] + self.root = None + self.force = 0 + self.data_files = self.distribution.data_files + self.warn_dir = 1 + + def finalize_options(self): + self.set_undefined_options('install', + ('install_data', 'install_dir'), + ('root', 'root'), + ('force', 'force'), + ) + + def run(self): + self.mkpath(self.install_dir) + for f in self.data_files: + if isinstance(f, str): + # it's a simple file, so copy it + f = convert_path(f) + if self.warn_dir: + self.warn("setup script did not provide a directory for " + "'%s' -- installing right in '%s'" % + (f, self.install_dir)) + (out, _) = self.copy_file(f, self.install_dir) + self.outfiles.append(out) + else: + # it's a tuple with path to install to and a list of files + dir = convert_path(f[0]) + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = change_root(self.root, dir) + self.mkpath(dir) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.data_files or [] + + def get_outputs(self): + return self.outfiles diff --git a/python/lib/distutils/distutils/command/install_egg_info.py b/python/lib/distutils/distutils/command/install_egg_info.py new file mode 100644 index 00000000..c8880310 --- /dev/null +++ b/python/lib/distutils/distutils/command/install_egg_info.py @@ -0,0 +1,78 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%s.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + sys.version[:3] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) + log.info("Writing %s", target) + if not self.dry_run: + f = open(target, 'w') + self.distribution.metadata.write_pkg_file(f) + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/python/lib/distutils/distutils/command/install_headers.py b/python/lib/distutils/distutils/command/install_headers.py new file mode 100644 index 00000000..d892416a --- /dev/null +++ b/python/lib/distutils/distutils/command/install_headers.py @@ -0,0 +1,51 @@ +"""distutils.command.install_headers + +Implements the Distutils 'install_headers' command, to install C/C++ header +files to the Python include directory.""" + +__revision__ = "$Id$" + +from distutils.core import Command + + +# XXX force is never used +class install_headers(Command): + + description = "install C/C++ header files" + + user_options = [('install-dir=', 'd', + "directory to install header files to"), + ('force', 'f', + "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.outfiles = [] + + def finalize_options(self): + self.set_undefined_options('install', + ('install_headers', 'install_dir'), + ('force', 'force')) + + + def run(self): + headers = self.distribution.headers + if not headers: + return + + self.mkpath(self.install_dir) + for header in headers: + (out, _) = self.copy_file(header, self.install_dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.distribution.headers or [] + + def get_outputs(self): + return self.outfiles + +# class install_headers diff --git a/python/lib/distutils/distutils/command/install_lib.py b/python/lib/distutils/distutils/command/install_lib.py new file mode 100644 index 00000000..043e8b6e --- /dev/null +++ b/python/lib/distutils/distutils/command/install_lib.py @@ -0,0 +1,219 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + +__revision__ = "$Id$" + +import os +import sys + +from distutils.core import Command +from distutils.errors import DistutilsOptionError + + +# Extension for Python source files. +if hasattr(os, 'extsep'): + PYTHON_SOURCE_EXTENSION = os.extsep + "py" +else: + PYTHON_SOURCE_EXTENSION = ".py" + +class install_lib(Command): + + description = "install all Python modules (extensions and pure Python)" + + # The byte-compilation options are a tad confusing. Here are the + # possible scenarios: + # 1) no compilation at all (--no-compile --no-optimize) + # 2) compile .pyc only (--compile --no-optimize; default) + # 3) compile .pyc and "level 1" .pyo (--compile --optimize) + # 4) compile "level 1" .pyo only (--no-compile --optimize) + # 5) compile .pyc and "level 2" .pyo (--compile --optimize-more) + # 6) compile "level 2" .pyo only (--no-compile --optimize-more) + # + # The UI for this is two option, 'compile' and 'optimize'. + # 'compile' is strictly boolean, and only decides whether to + # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and + # decides both whether to generate .pyo files and what level of + # optimization to use. + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'compile', 'skip-build'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + # let the 'install' command dictate our installation directory + self.install_dir = None + self.build_dir = None + self.force = 0 + self.compile = None + self.optimize = None + self.skip_build = None + + def finalize_options(self): + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. + self.set_undefined_options('install', + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir'), + ('force', 'force'), + ('compile', 'compile'), + ('optimize', 'optimize'), + ('skip_build', 'skip_build'), + ) + + if self.compile is None: + self.compile = 1 + if self.optimize is None: + self.optimize = 0 + + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if self.optimize not in (0, 1, 2): + raise AssertionError + except (ValueError, AssertionError): + raise DistutilsOptionError, "optimize must be 0, 1, or 2" + + def run(self): + # Make sure we have built everything we need first + self.build() + + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) + outfiles = self.install() + + # (Optionally) compile .py to .pyc + if outfiles is not None and self.distribution.has_pure_modules(): + self.byte_compile(outfiles) + + # -- Top-level worker functions ------------------------------------ + # (called from 'run()') + + def build(self): + if not self.skip_build: + if self.distribution.has_pure_modules(): + self.run_command('build_py') + if self.distribution.has_ext_modules(): + self.run_command('build_ext') + + def install(self): + if os.path.isdir(self.build_dir): + outfiles = self.copy_tree(self.build_dir, self.install_dir) + else: + self.warn("'%s' does not exist -- no Python modules to install" % + self.build_dir) + return + return outfiles + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + + # Get the "--root" directory supplied to the "install" command, + # and use it as a prefix to strip off the purported filename + # encoded in bytecode files. This is far from complete, but it + # should at least generate usable bytecode in RPM distributions. + install_root = self.get_finalized_command('install').root + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=install_root, + dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=install_root, + verbose=self.verbose, dry_run=self.dry_run) + + + # -- Utility methods ----------------------------------------------- + + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): + if not has_any: + return [] + + build_cmd = self.get_finalized_command(build_cmd) + build_files = build_cmd.get_outputs() + build_dir = getattr(build_cmd, cmd_option) + + prefix_len = len(build_dir) + len(os.sep) + outputs = [] + for file in build_files: + outputs.append(os.path.join(output_dir, file[prefix_len:])) + + return outputs + + def _bytecode_filenames(self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + # Since build_py handles package data installation, the + # list of outputs can contain more than just .py files. + # Make sure we only report bytecode for the .py files. + ext = os.path.splitext(os.path.normcase(py_file))[1] + if ext != PYTHON_SOURCE_EXTENSION: + continue + if self.compile: + bytecode_files.append(py_file + "c") + if self.optimize > 0: + bytecode_files.append(py_file + "o") + + return bytecode_files + + + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_outputs(self): + """Return the list of files that would be installed if this command + were actually run. Not affected by the "dry-run" flag or whether + modules have actually been built yet. + """ + pure_outputs = \ + self._mutate_outputs(self.distribution.has_pure_modules(), + 'build_py', 'build_lib', + self.install_dir) + if self.compile: + bytecode_outputs = self._bytecode_filenames(pure_outputs) + else: + bytecode_outputs = [] + + ext_outputs = \ + self._mutate_outputs(self.distribution.has_ext_modules(), + 'build_ext', 'build_lib', + self.install_dir) + + return pure_outputs + bytecode_outputs + ext_outputs + + def get_inputs(self): + """Get the list of files that are input to this command, ie. the + files that get installed as they are named in the build tree. + The files in this list correspond one-to-one to the output + filenames returned by 'get_outputs()'. + """ + inputs = [] + + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + inputs.extend(build_py.get_outputs()) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + inputs.extend(build_ext.get_outputs()) + + return inputs diff --git a/python/lib/distutils/distutils/command/install_scripts.py b/python/lib/distutils/distutils/command/install_scripts.py new file mode 100644 index 00000000..29cd9e7a --- /dev/null +++ b/python/lib/distutils/distutils/command/install_scripts.py @@ -0,0 +1,64 @@ +"""distutils.command.install_scripts + +Implements the Distutils 'install_scripts' command, for installing +Python scripts.""" + +# contributed by Bastian Kleineidam + +__revision__ = "$Id$" + +import os +from distutils.core import Command +from distutils import log +from stat import ST_MODE + +class install_scripts (Command): + + description = "install scripts (Python or otherwise)" + + user_options = [ + ('install-dir=', 'd', "directory to install scripts to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'skip-build'] + + + def initialize_options (self): + self.install_dir = None + self.force = 0 + self.build_dir = None + self.skip_build = None + + def finalize_options (self): + self.set_undefined_options('build', ('build_scripts', 'build_dir')) + self.set_undefined_options('install', + ('install_scripts', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build'), + ) + + def run (self): + if not self.skip_build: + self.run_command('build_scripts') + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + for file in self.get_outputs(): + if self.dry_run: + log.info("changing mode of %s", file) + else: + mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + + def get_inputs (self): + return self.distribution.scripts or [] + + def get_outputs(self): + return self.outfiles or [] + +# class install_scripts diff --git a/python/lib/distutils/distutils/command/register.py b/python/lib/distutils/distutils/command/register.py new file mode 100644 index 00000000..edb42b95 --- /dev/null +++ b/python/lib/distutils/distutils/command/register.py @@ -0,0 +1,315 @@ +"""distutils.command.register + +Implements the Distutils 'register' command (register with the repository). +""" + +# created 2002/10/21, Richard Jones + +__revision__ = "$Id$" + +import urllib2 +import getpass +import urlparse +from warnings import warn + +from distutils.core import PyPIRCCommand +from distutils import log + +class register(PyPIRCCommand): + + description = ("register the distribution with the Python package index") + user_options = PyPIRCCommand.user_options + [ + ('list-classifiers', None, + 'list the valid Trove classifiers'), + ('strict', None , + 'Will stop the registering if the meta-data are not fully compliant') + ] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers', 'strict'] + + sub_commands = [('check', lambda self: True)] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = {'strict': ('register', self.strict), + 'restructuredtext': ('register', 1)} + self.distribution.command_options['check'] = check_options + + def run(self): + self.finalize_options() + self._set_config() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.dry_run: + self.verify_metadata() + elif self.list_classifiers: + self.classifiers() + else: + self.send_metadata() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.register.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() + + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + + def classifiers(self): + ''' Fetch the list of classifiers from the server. + ''' + response = urllib2.urlopen(self.repository+'?:action=list_classifiers') + log.info(response.read()) + + def verify_metadata(self): + ''' Send the metadata to the package index server to be checked. + ''' + # send the info to the server and report the result + (code, result) = self.post_to_server(self.build_post_data('verify')) + log.info('Server response (%s): %s' % (code, result)) + + + def send_metadata(self): + ''' Send the metadata to the package index server. + + Well, do the following: + 1. figure who the user is, and then + 2. send the data as a Basic auth'ed POST. + + First we try to read the username/password from $HOME/.pypirc, + which is a ConfigParser-formatted file with a section + [distutils] containing username and password entries (both + in clear text). Eg: + + [distutils] + index-servers = + pypi + + [pypi] + username: fred + password: sekrit + + Otherwise, to figure who the user is, we offer the user three + choices: + + 1. use existing login, + 2. register as a new user, or + 3. set the password to a random string and email the user. + + ''' + # see if we can short-cut and get the username/password from the + # config + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' + + # get the user's login info + choices = '1 2 3 4'.split() + while choice not in choices: + self.announce('''\ +We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit +Your selection [default 1]: ''', log.INFO) + + choice = raw_input() + if not choice: + choice = '1' + elif choice not in choices: + print 'Please choose one of the four options!' + + if choice == '1': + # get the username and password + while not username: + username = raw_input('Username: ') + while not password: + password = getpass.getpass('Password: ') + + # set up the authentication + auth = urllib2.HTTPPasswordMgr() + host = urlparse.urlparse(self.repository)[1] + auth.add_password(self.realm, host, username, password) + # send the info to the server and report the result + code, result = self.post_to_server(self.build_post_data('submit'), + auth) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) + + # possibly save the login + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' + while choice.lower() not in 'yn': + choice = raw_input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) + + elif choice == '2': + data = {':action': 'user'} + data['name'] = data['password'] = data['email'] = '' + data['confirm'] = None + while not data['name']: + data['name'] = raw_input('Username: ') + while data['password'] != data['confirm']: + while not data['password']: + data['password'] = getpass.getpass('Password: ') + while not data['confirm']: + data['confirm'] = getpass.getpass(' Confirm: ') + if data['password'] != data['confirm']: + data['password'] = '' + data['confirm'] = None + print "Password and confirm don't match!" + while not data['email']: + data['email'] = raw_input(' EMail: ') + code, result = self.post_to_server(data) + if code != 200: + log.info('Server response (%s): %s' % (code, result)) + else: + log.info('You will receive an email shortly.') + log.info(('Follow the instructions in it to ' + 'complete registration.')) + elif choice == '3': + data = {':action': 'password_reset'} + data['email'] = '' + while not data['email']: + data['email'] = raw_input('Your email address: ') + code, result = self.post_to_server(data) + log.info('Server response (%s): %s' % (code, result)) + + def build_post_data(self, action): + # figure the data to send - the metadata plus some additional + # information used by the package server + meta = self.distribution.metadata + data = { + ':action': action, + 'metadata_version' : '1.0', + 'name': meta.get_name(), + 'version': meta.get_version(), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + if data['provides'] or data['requires'] or data['obsoletes']: + data['metadata_version'] = '1.1' + return data + + def post_to_server(self, data, auth=None): + ''' Post a query to the server, and return a string response. + ''' + if 'name' in data: + self.announce('Registering %s to %s' % (data['name'], + self.repository), + log.INFO) + # Build up the MIME payload for the urllib2 POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + chunks = [] + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) not in (type([]), type( () )): + value = [value] + for value in value: + chunks.append(sep_boundary) + chunks.append('\nContent-Disposition: form-data; name="%s"'%key) + chunks.append("\n\n") + chunks.append(value) + if value and value[-1] == '\r': + chunks.append('\n') # write an extra newline (lurve Macs) + chunks.append(end_boundary) + chunks.append("\n") + + # chunks may be bytes (str) or unicode objects that we need to encode + body = [] + for chunk in chunks: + if isinstance(chunk, unicode): + body.append(chunk.encode('utf-8')) + else: + body.append(chunk) + + body = ''.join(body) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, + 'Content-length': str(len(body)) + } + req = urllib2.Request(self.repository, body, headers) + + # handle HTTP and include the Basic Auth handler + opener = urllib2.build_opener( + urllib2.HTTPBasicAuthHandler(password_mgr=auth) + ) + data = '' + try: + result = opener.open(req) + except urllib2.HTTPError, e: + if self.show_response: + data = e.fp.read() + result = e.code, e.msg + except urllib2.URLError, e: + result = 500, str(e) + else: + if self.show_response: + data = result.read() + result = 200, 'OK' + if self.show_response: + dashes = '-' * 75 + self.announce('%s%s%s' % (dashes, data, dashes)) + + return result diff --git a/python/lib/distutils/distutils/command/sdist.py b/python/lib/distutils/distutils/command/sdist.py new file mode 100644 index 00000000..821420d6 --- /dev/null +++ b/python/lib/distutils/distutils/command/sdist.py @@ -0,0 +1,477 @@ +"""distutils.command.sdist + +Implements the Distutils 'sdist' command (create a source distribution).""" + +__revision__ = "$Id$" + +import os +import string +import sys +from glob import glob +from warnings import warn + +from distutils.core import Command +from distutils import dir_util, dep_util, file_util, archive_util +from distutils.text_file import TextFile +from distutils.errors import (DistutilsPlatformError, DistutilsOptionError, + DistutilsTemplateError) +from distutils.filelist import FileList +from distutils import log +from distutils.util import convert_path + +def show_formats(): + """Print all possible values for the 'formats' option (used by + the "--help-formats" command-line option). + """ + from distutils.fancy_getopt import FancyGetopt + from distutils.archive_util import ARCHIVE_FORMATS + formats = [] + for format in ARCHIVE_FORMATS.keys(): + formats.append(("formats=" + format, None, + ARCHIVE_FORMATS[format][2])) + formats.sort() + FancyGetopt(formats).print_help( + "List of available source distribution formats:") + +class sdist(Command): + + description = "create a source distribution (tarball, zip file, etc.)" + + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + + user_options = [ + ('template=', 't', + "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', + "name of manifest file [default: MANIFEST]"), + ('use-defaults', None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]"), + ('no-defaults', None, + "don't include the default file set"), + ('prune', None, + "specifically exclude files/directories that should not be " + "distributed (build tree, RCS/CVS dirs, etc.) " + "[default; disable with --no-prune]"), + ('no-prune', None, + "don't automatically exclude anything"), + ('manifest-only', 'o', + "just regenerate the manifest and then stop " + "(implies --force-manifest)"), + ('force-manifest', 'f', + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ('metadata-check', None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), + ] + + boolean_options = ['use-defaults', 'prune', + 'manifest-only', 'force-manifest', + 'keep-temp', 'metadata-check'] + + help_options = [ + ('help-formats', None, + "list available distribution formats", show_formats), + ] + + negative_opt = {'no-defaults': 'use-defaults', + 'no-prune': 'prune' } + + default_format = {'posix': 'gztar', + 'nt': 'zip' } + + sub_commands = [('check', checking_metadata)] + + def initialize_options(self): + # 'template' and 'manifest' are, respectively, the names of + # the manifest template and manifest file. + self.template = None + self.manifest = None + + # 'use_defaults': if true, we will include the default file set + # in the manifest + self.use_defaults = 1 + self.prune = 1 + + self.manifest_only = 0 + self.force_manifest = 0 + + self.formats = None + self.keep_temp = 0 + self.dist_dir = None + + self.archive_files = None + self.metadata_check = 1 + self.owner = None + self.group = None + + def finalize_options(self): + if self.manifest is None: + self.manifest = "MANIFEST" + if self.template is None: + self.template = "MANIFEST.in" + + self.ensure_string_list('formats') + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError, \ + "don't know how to create source distributions " + \ + "on platform %s" % os.name + + bad_format = archive_util.check_archive_formats(self.formats) + if bad_format: + raise DistutilsOptionError, \ + "unknown archive format '%s'" % bad_format + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # 'filelist' contains the list of files that will make up the + # manifest + self.filelist = FileList() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Do whatever it takes to get the list of files to process + # (process the manifest template, read an existing manifest, + # whatever). File list is accumulated in 'self.filelist'. + self.get_file_list() + + # If user just wanted us to regenerate the manifest, stop now. + if self.manifest_only: + return + + # Otherwise, go ahead and create the source distribution tarball, + # or zipfile, or whatever. + self.make_distribution() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() + + def get_file_list(self): + """Figure out the list of files to include in the source + distribution, and put it in 'self.filelist'. This might involve + reading the manifest template (and writing the manifest), or just + reading the manifest, or just using the default file set -- it all + depends on the user's options. + """ + # new behavior when using a template: + # the file list is recalculated every time because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior with templates. + template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + + if template_exists: + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ + + standards = [('README', 'README.txt'), self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = 0 + for fn in alts: + if os.path.exists(fn): + got_it = 1 + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + string.join(alts, ', ')) + else: + if os.path.exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = filter(os.path.isfile, glob(pattern)) + if files: + self.filelist.extend(files) + + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files + if self.distribution.has_pure_modules(): + self.filelist.extend(build_py.get_source_files()) + + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + # getting distribution.data_files + if self.distribution.has_data_files(): + for item in self.distribution.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + self.filelist.append(f) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + def read_template(self): + """Read and parse manifest template file named by self.template. + + (usually "MANIFEST.in") The parsing and processing is done by + 'self.filelist', which updates itself accordingly. + """ + log.info("reading manifest template '%s'", self.template) + template = TextFile(self.template, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + collapse_join=1) + + try: + while 1: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) + finally: + template.close() + + def prune_file_list(self): + """Prune off branches that might slip into the file list as created + by 'read_template()', but really don't belong there: + * the build tree (typically "build") + * the release tree itself (only an issue if we ran "sdist" + previously with --keep-temp, or it aborted) + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories + """ + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + + # pruning out vcs directories + # both separators are used under win32 + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) + + def write_manifest(self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + if self._manifest_is_not_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), + "writing manifest file '%s'" % self.manifest) + + def _manifest_is_not_generated(self): + # check for special comment used in 2.7.1 and higher + if not os.path.isfile(self.manifest): + return False + + fp = open(self.manifest, 'rU') + try: + first_line = fp.readline() + finally: + fp.close() + return first_line != '# file GENERATED by distutils, do NOT edit\n' + + def read_manifest(self): + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.filelist', the list of files to include in the source + distribution. + """ + log.info("reading manifest file '%s'", self.manifest) + manifest = open(self.manifest) + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + manifest.close() + + def make_release_tree(self, base_dir, files): + """Create the directory tree that will become the source + distribution archive. All directories implied by the filenames in + 'files' are created under 'base_dir', and then we hard link or copy + (if hard linking is unavailable) those files into place. + Essentially, this duplicates the developer's source tree, but in a + directory named after the distribution, containing only the files + to be distributed. + """ + # Create all the directories under 'base_dir' necessary to + # put 'files' there; the 'mkpath()' is just so we don't die + # if the manifest happens to be empty. + self.mkpath(base_dir) + dir_util.create_tree(base_dir, files, dry_run=self.dry_run) + + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) + + if hasattr(os, 'link'): # can make hard links on this system + link = 'hard' + msg = "making hard links in %s..." % base_dir + else: # nope, have to copy + link = None + msg = "copying files to %s..." % base_dir + + if not files: + log.warn("no files to distribute -- empty manifest?") + else: + log.info(msg) + for file in files: + if not os.path.isfile(file): + log.warn("'%s' not a regular file -- skipping" % file) + else: + dest = os.path.join(base_dir, file) + self.copy_file(file, dest, link=link) + + self.distribution.metadata.write_pkg_info(base_dir) + + def make_distribution(self): + """Create the source distribution(s). First, we create the release + tree with 'make_release_tree()'; then, we create all required + archive files (according to 'self.formats') from the release tree. + Finally, we clean up by blowing away the release tree (unless + 'self.keep_temp' is true). The list of archive files created is + stored so it can be retrieved later by 'get_archive_files()'. + """ + # Don't warn about missing meta-data here -- should be (and is!) + # done elsewhere. + base_dir = self.distribution.get_fullname() + base_name = os.path.join(self.dist_dir, base_dir) + + self.make_release_tree(base_dir, self.filelist.files) + archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + + for fmt in self.formats: + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) + archive_files.append(file) + self.distribution.dist_files.append(('sdist', '', file)) + + self.archive_files = archive_files + + if not self.keep_temp: + dir_util.remove_tree(base_dir, dry_run=self.dry_run) + + def get_archive_files(self): + """Return the list of archive files created when the command + was run, or None if the command hasn't run yet. + """ + return self.archive_files diff --git a/python/lib/distutils/distutils/command/upload.py b/python/lib/distutils/distutils/command/upload.py new file mode 100644 index 00000000..d0133353 --- /dev/null +++ b/python/lib/distutils/distutils/command/upload.py @@ -0,0 +1,194 @@ +"""distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to PyPI).""" +import os +import socket +import platform +from urllib2 import urlopen, Request, HTTPError +from base64 import standard_b64encode +import urlparse +import cStringIO as StringIO +from hashlib import md5 + +from distutils.errors import DistutilsOptionError +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log + +class upload(PyPIRCCommand): + + description = "upload binary package to PyPI" + + user_options = PyPIRCCommand.user_options + [ + ('sign', 's', + 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), + ] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.username = '' + self.password = '' + self.show_response = 0 + self.sign = False + self.identity = None + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + + def run(self): + if not self.distribution.dist_files: + raise DistutilsOptionError("No dist file created in earlier command") + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() + meta = self.distribution.metadata + data = { + # action + ':action': 'file_upload', + 'protcol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename),content), + 'filetype': command, + 'pyversion': pyversion, + 'md5_digest': md5(content).hexdigest(), + + # additional meta-data + 'metadata_version' : '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + comment = '' + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc").read()) + + # set up the authentication + auth = "Basic " + standard_b64encode(self.username + ":" + + self.password) + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + if isinstance(value, tuple): + fn = ';filename="%s"' % value[0] + value = value[1] + else: + fn = "" + + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write(fn) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + + # build the Request + headers = {'Content-type': + 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth} + + request = Request(self.repository, data=body, + headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + if self.show_response: + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) + self.announce(msg, log.INFO) + except socket.error, e: + self.announce(str(e), log.ERROR) + return + except HTTPError, e: + status = e.code + reason = e.msg + + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (status, reason), + log.ERROR) diff --git a/python/lib/distutils/distutils/config.py b/python/lib/distutils/distutils/config.py index afa403f2..1d327143 100644 --- a/python/lib/distutils/distutils/config.py +++ b/python/lib/distutils/distutils/config.py @@ -42,16 +42,11 @@ class PyPIRCCommand(Command): def _store_pypirc(self, username, password): """Creates a default .pypirc file.""" rc = self._get_rc_file() - f = open(rc, 'w') + f = os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0600), 'w') try: f.write(DEFAULT_PYPIRC % (username, password)) finally: f.close() - try: - os.chmod(rc, 0600) - except OSError: - # should do something better here - pass def _read_pypirc(self): """Reads the .pypirc file.""" diff --git a/python/lib/distutils/distutils/cygwinccompiler.py b/python/lib/distutils/distutils/cygwinccompiler.py index a1ee815c..5d116876 100644 --- a/python/lib/distutils/distutils/cygwinccompiler.py +++ b/python/lib/distutils/distutils/cygwinccompiler.py @@ -319,13 +319,18 @@ class Mingw32CCompiler (CygwinCCompiler): else: entry_point = '' - self.set_executables(compiler='gcc -mno-cygwin -O -Wall', - compiler_so='gcc -mno-cygwin -mdll -O -Wall', - compiler_cxx='g++ -mno-cygwin -O -Wall', - linker_exe='gcc -mno-cygwin', - linker_so='%s -mno-cygwin %s %s' - % (self.linker_dll, shared_option, - entry_point)) + if self.gcc_version < '4' or is_cygwingcc(): + no_cygwin = ' -mno-cygwin' + else: + no_cygwin = '' + + self.set_executables(compiler='gcc%s -O -Wall' % no_cygwin, + compiler_so='gcc%s -mdll -O -Wall' % no_cygwin, + compiler_cxx='g++%s -O -Wall' % no_cygwin, + linker_exe='gcc%s' % no_cygwin, + linker_so='%s%s %s %s' + % (self.linker_dll, no_cygwin, + shared_option, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') @@ -447,3 +452,12 @@ def get_versions(): else: dllwrap_version = None return (gcc_version, ld_version, dllwrap_version) + +def is_cygwingcc(): + '''Try to determine if the gcc that would be used is from cygwin.''' + out = os.popen('gcc -dumpmachine', 'r') + out_string = out.read() + out.close() + # out_string is the target triplet cpu-vendor-os + # Cygwin's gcc sets the os to 'cygwin' + return out_string.strip().endswith('cygwin') diff --git a/python/lib/distutils/distutils/dep_util.py b/python/lib/distutils/distutils/dep_util.py index 4e40df68..2b759056 100644 --- a/python/lib/distutils/distutils/dep_util.py +++ b/python/lib/distutils/distutils/dep_util.py @@ -7,6 +7,7 @@ timestamp dependency analysis.""" __revision__ = "$Id$" import os +from stat import ST_MTIME from distutils.errors import DistutilsFileError def newer(source, target): @@ -27,7 +28,7 @@ def newer(source, target): if not os.path.exists(target): return True - return os.stat(source).st_mtime > os.stat(target).st_mtime + return os.stat(source)[ST_MTIME] > os.stat(target)[ST_MTIME] def newer_pairwise(sources, targets): """Walk two filename lists in parallel, testing if each source is newer @@ -71,7 +72,7 @@ def newer_group(sources, target, missing='error'): # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. - target_mtime = os.stat(target).st_mtime + target_mtime = os.stat(target)[ST_MTIME] for source in sources: if not os.path.exists(source): @@ -82,7 +83,7 @@ def newer_group(sources, target, missing='error'): elif missing == 'newer': # missing source means target is return True # out-of-date - if os.stat(source).st_mtime > target_mtime: + if os.stat(source)[ST_MTIME] > target_mtime: return True return False diff --git a/python/lib/distutils/distutils/dir_util.py b/python/lib/distutils/distutils/dir_util.py index 9c5cf337..5026e246 100644 --- a/python/lib/distutils/distutils/dir_util.py +++ b/python/lib/distutils/distutils/dir_util.py @@ -144,6 +144,10 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1, src_name = os.path.join(src, n) dst_name = os.path.join(dst, n) + if n.startswith('.nfs'): + # skip NFS rename files + continue + if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) if verbose >= 1: diff --git a/python/lib/distutils/distutils/dist.py b/python/lib/distutils/distutils/dist.py index 597909ea..e025313d 100644 --- a/python/lib/distutils/distutils/dist.py +++ b/python/lib/distutils/distutils/dist.py @@ -1111,7 +1111,8 @@ class DistributionMetadata: """Write the PKG-INFO format data to a file object. """ version = '1.0' - if self.provides or self.requires or self.obsoletes: + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): version = '1.1' self._write_field(file, 'Metadata-Version', version) diff --git a/python/lib/distutils/distutils/filelist.py b/python/lib/distutils/distutils/filelist.py index 4aac6d39..2f1c457e 100644 --- a/python/lib/distutils/distutils/filelist.py +++ b/python/lib/distutils/distutils/filelist.py @@ -210,6 +210,7 @@ class FileList: Return 1 if files are found. """ + # XXX docstring lying about what the special chars are? files_found = 0 pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % @@ -297,11 +298,14 @@ def glob_to_re(pattern): # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters. - # XXX currently the "special characters" are just slash -- i.e. this is - # Unix-only. - pattern_re = re.sub(r'((?\s*" manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + manifest_f = open(manifest_file, 'w') try: manifest_f.write(manifest_buf) + return manifest_file finally: manifest_f.close() except IOError: diff --git a/python/lib/distutils/distutils/spawn.py b/python/lib/distutils/distutils/spawn.py index 5c014c4b..7306099f 100644 --- a/python/lib/distutils/distutils/spawn.py +++ b/python/lib/distutils/distutils/spawn.py @@ -96,17 +96,43 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) +if sys.platform == 'darwin': + from distutils import sysconfig + _cfg_target = None + _cfg_target_split = None def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv + exec_args = [cmd[0], cmd] + if sys.platform == 'darwin': + global _cfg_target, _cfg_target_split + if _cfg_target is None: + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' + if _cfg_target: + _cfg_target_split = [int(x) for x in _cfg_target.split('.')] + if _cfg_target: + # ensure that the deployment target of build process is not less + # than that used when the interpreter was built. This ensures + # extension modules are built with correct compatibility values + cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) + if _cfg_target_split > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' + 'now "%s" but "%s" during configure' + % (cur_target, _cfg_target)) + raise DistutilsPlatformError(my_msg) + env = dict(os.environ, + MACOSX_DEPLOYMENT_TARGET=cur_target) + exec_fn = search_path and os.execvpe or os.execve + exec_args.append(env) pid = os.fork() if pid == 0: # in the child try: - exec_fn(cmd[0], cmd) + exec_fn(*exec_args) except OSError, e: sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) diff --git a/python/lib/distutils/distutils/sysconfig.py b/python/lib/distutils/distutils/sysconfig.py index d206e0cd..4aa93346 100644 --- a/python/lib/distutils/distutils/sysconfig.py +++ b/python/lib/distutils/distutils/sysconfig.py @@ -37,6 +37,11 @@ if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, os.path.pardir)) +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + # this is the build directory, at least for posix + project_base = os.path.normpath(os.environ["_PYTHON_PROJECT_BASE"]) + # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. @@ -142,6 +147,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "on platform '%s'" % os.name) + def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -149,12 +155,35 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO') + 'CCSHARED', 'LDSHARED', 'SO', 'AR', + 'ARFLAGS') if 'CC' in os.environ: - cc = os.environ['CC'] + newcc = os.environ['CC'] + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc):] + cc = newcc if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: @@ -172,6 +201,12 @@ def customize_compiler(compiler): cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -180,7 +215,8 @@ def customize_compiler(compiler): compiler_so=cc_cmd + ' ' + ccshared, compiler_cxx=cxx, linker_so=ldshared, - linker_exe=cc) + linker_exe=cc, + archiver=archiver) compiler.shared_lib_extension = so_ext @@ -205,7 +241,7 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(project_base, "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") @@ -357,81 +393,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - parse_config_h(file(filename), g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On MacOSX we need to check the setting of the environment variable - # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so - # it needs to be compatible. - # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: - cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] - cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') - if cur_target == '': - cur_target = cfg_target - os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target - elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' - % (cur_target, cfg_target)) - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - - elif get_python_version() < '2.1': - # The following two branches are for 1.5.2 compatibility. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - - elif sys.platform == 'beos': - # Linker script is in the config directory. In the Makefile it is - # relative to the srcdir, which after installation no longer makes - # sense. - python_lib = get_python_lib(standard_lib=1) - linkerscript_path = string.split(g['LDSHARED'])[0] - linkerscript_name = os.path.basename(linkerscript_path) - linkerscript = os.path.join(python_lib, 'config', - linkerscript_name) - - # XXX this isn't the right place to do this: adding the Python - # library to the link, if needed, should be in the "build_ext" - # command. (It's also needed for non-MS compilers on Windows, and - # it's taken care of for them by the 'build_ext.get_libraries()' - # method.) - g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, get_python_version())) - + # _sysconfigdata is generated at build time, see the sysconfig module + from _sysconfigdata import build_time_vars global _config_vars - _config_vars = g + _config_vars = {} + _config_vars.update(build_time_vars) def _init_nt(): @@ -494,66 +460,11 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags - - # If we're on OSX 10.5 or later and the user tries to - # compiles an extension using an SDK that is not present - # on the current machine it is better to not use an SDK - # than to fail. - # - # The major usecase for this is users using a Python.org - # binary installer on OSX 10.6: that installer uses - # the 10.4u SDK, but that SDK is not installed by default - # when you install Xcode. - # - m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) - if m is not None: - sdk = m.group(1) - if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) - _config_vars[key] = flags + import _osx_support + _osx_support.customize_config_vars(_config_vars) if args: vals = [] diff --git a/python/lib/distutils/distutils/unixccompiler.py b/python/lib/distutils/distutils/unixccompiler.py index c49ac9ba..2aa1cb1d 100644 --- a/python/lib/distutils/distutils/unixccompiler.py +++ b/python/lib/distutils/distutils/unixccompiler.py @@ -26,6 +26,9 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log +if sys.platform == 'darwin': + import _osx_support + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -41,68 +44,6 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. -def _darwin_compiler_fixup(compiler_so, cc_args): - """ - This function will strip '-isysroot PATH' and '-arch ARCH' from the - compile flags if the user has specified one them in extra_compile_flags. - - This is needed because '-arch ARCH' adds another architecture to the - build, without a way to remove an architecture. Furthermore GCC will - barf if multiple '-isysroot' arguments are present. - """ - stripArch = stripSysroot = 0 - - compiler_so = list(compiler_so) - kernel_version = os.uname()[2] # 8.4.3 - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # OSX before 10.4.0, these don't support -arch and -isysroot at - # all. - stripArch = stripSysroot = True - else: - stripArch = '-arch' in cc_args - stripSysroot = '-isysroot' in cc_args - - if stripArch or 'ARCHFLAGS' in os.environ: - while 1: - try: - index = compiler_so.index('-arch') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - break - - if 'ARCHFLAGS' in os.environ and not stripArch: - # User specified different -arch flags in the environ, - # see also distutils.sysconfig - compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() - - if stripSysroot: - try: - index = compiler_so.index('-isysroot') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - pass - - # Check if the SDK that is used during compilation actually exists, - # the universal build requires the usage of a universal SDK and not all - # users have that installed by default. - sysroot = None - if '-isysroot' in cc_args: - idx = cc_args.index('-isysroot') - sysroot = cc_args[idx+1] - elif '-isysroot' in compiler_so: - idx = compiler_so.index('-isysroot') - sysroot = compiler_so[idx+1] - - if sysroot and not os.path.isdir(sysroot): - log.warn("Compiling with an SDK that doesn't seem to exist: %s", - sysroot) - log.warn("Please check your Xcode installation") - - return compiler_so class UnixCCompiler(CCompiler): @@ -172,7 +113,8 @@ class UnixCCompiler(CCompiler): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so if sys.platform == 'darwin': - compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) @@ -251,7 +193,7 @@ class UnixCCompiler(CCompiler): linker[i] = self.compiler_cxx[i] if sys.platform == 'darwin': - linker = _darwin_compiler_fixup(linker, ld_args) + linker = _osx_support.compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) except DistutilsExecError, msg: diff --git a/python/lib/distutils/distutils/util.py b/python/lib/distutils/distutils/util.py index 6c49f0b1..ea6ed8a9 100644 --- a/python/lib/distutils/distutils/util.py +++ b/python/lib/distutils/distutils/util.py @@ -51,6 +51,10 @@ def get_platform (): return 'win-ia64' return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. @@ -76,6 +80,11 @@ def get_platform (): if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxint] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) @@ -88,94 +97,10 @@ def get_platform (): if m: release = m.group() elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - try: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - finally: - f.close() - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs = tuple(sorted(set(archs))) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxint >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxint >= 2**32: - machine = 'ppc64' + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) return "%s-%s-%s" % (osname, release, machine) diff --git a/python/lib/grizzled/grizzled/net/ftp/parse.py b/python/lib/grizzled/grizzled/net/ftp/parse.py index 7c2e7ecb..47967f49 100644 --- a/python/lib/grizzled/grizzled/net/ftp/parse.py +++ b/python/lib/grizzled/grizzled/net/ftp/parse.py @@ -44,7 +44,7 @@ __docformat__ = 'restructuredtext en' # --------------------------------------------------------------------------- import time -from enum import Enum +from deprecated_enum import Enum # --------------------------------------------------------------------------- # Exports diff --git a/python/lib/mox/mox_test.py b/python/lib/mox/mox_test.py index 98493390..accabf4a 100644 --- a/python/lib/mox/mox_test.py +++ b/python/lib/mox/mox_test.py @@ -284,7 +284,7 @@ class IsATest(unittest.TestCase): str_list = ["abc", "def"] self.assert_(isa_list == str_list) - def testEquailtyInListInvalid(self): + def testEqualityInListInvalid(self): """Verify list contents are properly compared.""" isa_list = [mox.IsA(str),mox.IsA(str)] mixed_list = ["abc", 123] diff --git a/python/lib/protorpc-1.0/protorpc/definition.py b/python/lib/protorpc-1.0/protorpc/definition.py index c0344d43..6029087d 100644 --- a/python/lib/protorpc-1.0/protorpc/definition.py +++ b/python/lib/protorpc-1.0/protorpc/definition.py @@ -21,7 +21,6 @@ __author__ = 'rafek@google.com (Rafe Kaplan)' import new import sys -import urllib2 from . import descriptor from . import message_types diff --git a/python/lib/sqlcmd/sqlcmd/__init__.py b/python/lib/sqlcmd/sqlcmd/__init__.py index f730ea4d..a5ce376d 100644 --- a/python/lib/sqlcmd/sqlcmd/__init__.py +++ b/python/lib/sqlcmd/sqlcmd/__init__.py @@ -74,7 +74,7 @@ from grizzled.log import WrappingLogFormatter from grizzled.misc import str2bool from grizzled import history -from enum import Enum +from deprecated_enum import Enum from sqlcmd.config import SQLCmdConfig from sqlcmd.exception import * diff --git a/python/php/sdk/google/appengine/datastore/datastore_v3_pb.php b/python/php/sdk/google/appengine/datastore/datastore_v3_pb.php index d750fdd3..0d92f278 100644 --- a/python/php/sdk/google/appengine/datastore/datastore_v3_pb.php +++ b/python/php/sdk/google/appengine/datastore/datastore_v3_pb.php @@ -5950,6 +5950,7 @@ namespace google\appengine_datastore_v3 { class DeleteRequest extends \google\net\ProtocolMessage { private $key = array(); private $snapshot = array(); + private $composite_index = array(); public function getTrusted() { if (!isset($this->trusted)) { return false; @@ -6107,6 +6108,37 @@ namespace google\appengine_datastore_v3 { public function hasHeader() { return isset($this->header); } + public function getCompositeIndexSize() { + return sizeof($this->composite_index); + } + public function getCompositeIndexList() { + return $this->composite_index; + } + public function mutableCompositeIndex($idx) { + if (!isset($this->composite_index[$idx])) { + $val = new \storage_onestore_v3\CompositeIndex(); + $this->composite_index[$idx] = $val; + return $val; + } + return $this->composite_index[$idx]; + } + public function getCompositeIndex($idx) { + if (isset($this->composite_index[$idx])) { + return $this->composite_index[$idx]; + } + if ($idx >= end(array_keys($this->composite_index))) { + throw new \OutOfRangeException('index out of range: ' + $idx); + } + return new \storage_onestore_v3\CompositeIndex(); + } + public function addCompositeIndex() { + $val = new \storage_onestore_v3\CompositeIndex(); + $this->composite_index[] = $val; + return $val; + } + public function clearCompositeIndex() { + $this->composite_index = array(); + } public function clear() { $this->clearTrusted(); $this->clearTransaction(); @@ -6115,6 +6147,7 @@ namespace google\appengine_datastore_v3 { $this->clearMarkChanges(); $this->clearSnapshot(); $this->clearHeader(); + $this->clearCompositeIndex(); } public function byteSizePartial() { $res = 0; @@ -6145,6 +6178,11 @@ namespace google\appengine_datastore_v3 { $res += 1; $res += $this->lengthString($this->header->byteSizePartial()); } + $this->checkProtoArray($this->composite_index); + $res += 1 * sizeof($this->composite_index); + foreach ($this->composite_index as $value) { + $res += $this->lengthString($value->byteSizePartial()); + } return $res; } public function outputPartial($out) { @@ -6182,6 +6220,12 @@ namespace google\appengine_datastore_v3 { $out->putVarInt32($this->header->byteSizePartial()); $this->header->outputPartial($out); } + $this->checkProtoArray($this->composite_index); + foreach ($this->composite_index as $value) { + $out->putVarInt32(90); + $out->putVarInt32($value->byteSizePartial()); + $value->outputPartial($out); + } } public function tryMerge($d) { while($d->avail() > 0) { @@ -6220,6 +6264,12 @@ namespace google\appengine_datastore_v3 { $d->skip($length); $this->mutableHeader()->tryMerge($tmp); break; + case 90: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->addCompositeIndex()->tryMerge($tmp); + break; case 0: throw new \google\net\ProtocolBufferDecodeError(); break; @@ -6237,6 +6287,9 @@ namespace google\appengine_datastore_v3 { if (!$value->isInitialized()) return 'snapshot'; } if (isset($this->header) && (!$this->header->isInitialized())) return 'header'; + foreach ($this->composite_index as $value) { + if (!$value->isInitialized()) return 'composite_index'; + } return null; } public function mergeFrom($x) { @@ -6262,6 +6315,9 @@ namespace google\appengine_datastore_v3 { if ($x->hasHeader()) { $this->mutableHeader()->mergeFrom($x->getHeader()); } + foreach ($x->getCompositeIndexList() as $v) { + $this->addCompositeIndex()->copyFrom($v); + } } public function equals($x) { if ($x === $this) { return true; } @@ -6283,6 +6339,10 @@ namespace google\appengine_datastore_v3 { } if (isset($this->header) !== isset($x->header)) return false; if (isset($this->header) && !$this->header->equals($x->header)) return false; + if (sizeof($this->composite_index) !== sizeof($x->composite_index)) return false; + foreach (array_map(null, $this->composite_index, $x->composite_index) as $v) { + if (!$v[0]->equals($v[1])) return false; + } return true; } public function shortDebugString($prefix = "") { @@ -6308,6 +6368,9 @@ namespace google\appengine_datastore_v3 { if (isset($this->header)) { $res .= $prefix . "header <\n" . $this->header->shortDebugString($prefix . " ") . $prefix . ">\n"; } + foreach ($this->composite_index as $value) { + $res .= $prefix . "composite_index <\n" . $value->shortDebugString($prefix . " ") . $prefix . ">\n"; + } return $res; } } diff --git a/python/php/sdk/google/appengine/datastore/datastore_v4_pb.php b/python/php/sdk/google/appengine/datastore/datastore_v4_pb.php index bfa13917..fe8f371b 100644 --- a/python/php/sdk/google/appengine/datastore/datastore_v4_pb.php +++ b/python/php/sdk/google/appengine/datastore/datastore_v4_pb.php @@ -1153,9 +1153,55 @@ namespace google\appengine\datastore\v4 { public function hasPropertyFilter() { return isset($this->property_filter); } + public function getBoundingCircleFilter() { + if (!isset($this->bounding_circle_filter)) { + return new \google\appengine\datastore\v4\BoundingCircleFilter(); + } + return $this->bounding_circle_filter; + } + public function mutableBoundingCircleFilter() { + if (!isset($this->bounding_circle_filter)) { + $res = new \google\appengine\datastore\v4\BoundingCircleFilter(); + $this->bounding_circle_filter = $res; + return $res; + } + return $this->bounding_circle_filter; + } + public function clearBoundingCircleFilter() { + if (isset($this->bounding_circle_filter)) { + unset($this->bounding_circle_filter); + } + } + public function hasBoundingCircleFilter() { + return isset($this->bounding_circle_filter); + } + public function getBoundingBoxFilter() { + if (!isset($this->bounding_box_filter)) { + return new \google\appengine\datastore\v4\BoundingBoxFilter(); + } + return $this->bounding_box_filter; + } + public function mutableBoundingBoxFilter() { + if (!isset($this->bounding_box_filter)) { + $res = new \google\appengine\datastore\v4\BoundingBoxFilter(); + $this->bounding_box_filter = $res; + return $res; + } + return $this->bounding_box_filter; + } + public function clearBoundingBoxFilter() { + if (isset($this->bounding_box_filter)) { + unset($this->bounding_box_filter); + } + } + public function hasBoundingBoxFilter() { + return isset($this->bounding_box_filter); + } public function clear() { $this->clearCompositeFilter(); $this->clearPropertyFilter(); + $this->clearBoundingCircleFilter(); + $this->clearBoundingBoxFilter(); } public function byteSizePartial() { $res = 0; @@ -1167,6 +1213,14 @@ namespace google\appengine\datastore\v4 { $res += 1; $res += $this->lengthString($this->property_filter->byteSizePartial()); } + if (isset($this->bounding_circle_filter)) { + $res += 1; + $res += $this->lengthString($this->bounding_circle_filter->byteSizePartial()); + } + if (isset($this->bounding_box_filter)) { + $res += 1; + $res += $this->lengthString($this->bounding_box_filter->byteSizePartial()); + } return $res; } public function outputPartial($out) { @@ -1180,6 +1234,16 @@ namespace google\appengine\datastore\v4 { $out->putVarInt32($this->property_filter->byteSizePartial()); $this->property_filter->outputPartial($out); } + if (isset($this->bounding_circle_filter)) { + $out->putVarInt32(26); + $out->putVarInt32($this->bounding_circle_filter->byteSizePartial()); + $this->bounding_circle_filter->outputPartial($out); + } + if (isset($this->bounding_box_filter)) { + $out->putVarInt32(34); + $out->putVarInt32($this->bounding_box_filter->byteSizePartial()); + $this->bounding_box_filter->outputPartial($out); + } } public function tryMerge($d) { while($d->avail() > 0) { @@ -1197,6 +1261,18 @@ namespace google\appengine\datastore\v4 { $d->skip($length); $this->mutablePropertyFilter()->tryMerge($tmp); break; + case 26: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableBoundingCircleFilter()->tryMerge($tmp); + break; + case 34: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableBoundingBoxFilter()->tryMerge($tmp); + break; case 0: throw new \google\net\ProtocolBufferDecodeError(); break; @@ -1208,6 +1284,8 @@ namespace google\appengine\datastore\v4 { public function checkInitialized() { if (isset($this->composite_filter) && (!$this->composite_filter->isInitialized())) return 'composite_filter'; if (isset($this->property_filter) && (!$this->property_filter->isInitialized())) return 'property_filter'; + if (isset($this->bounding_circle_filter) && (!$this->bounding_circle_filter->isInitialized())) return 'bounding_circle_filter'; + if (isset($this->bounding_box_filter) && (!$this->bounding_box_filter->isInitialized())) return 'bounding_box_filter'; return null; } public function mergeFrom($x) { @@ -1218,6 +1296,12 @@ namespace google\appengine\datastore\v4 { if ($x->hasPropertyFilter()) { $this->mutablePropertyFilter()->mergeFrom($x->getPropertyFilter()); } + if ($x->hasBoundingCircleFilter()) { + $this->mutableBoundingCircleFilter()->mergeFrom($x->getBoundingCircleFilter()); + } + if ($x->hasBoundingBoxFilter()) { + $this->mutableBoundingBoxFilter()->mergeFrom($x->getBoundingBoxFilter()); + } } public function equals($x) { if ($x === $this) { return true; } @@ -1225,6 +1309,10 @@ namespace google\appengine\datastore\v4 { if (isset($this->composite_filter) && !$this->composite_filter->equals($x->composite_filter)) return false; if (isset($this->property_filter) !== isset($x->property_filter)) return false; if (isset($this->property_filter) && !$this->property_filter->equals($x->property_filter)) return false; + if (isset($this->bounding_circle_filter) !== isset($x->bounding_circle_filter)) return false; + if (isset($this->bounding_circle_filter) && !$this->bounding_circle_filter->equals($x->bounding_circle_filter)) return false; + if (isset($this->bounding_box_filter) !== isset($x->bounding_box_filter)) return false; + if (isset($this->bounding_box_filter) && !$this->bounding_box_filter->equals($x->bounding_box_filter)) return false; return true; } public function shortDebugString($prefix = "") { @@ -1235,6 +1323,12 @@ namespace google\appengine\datastore\v4 { if (isset($this->property_filter)) { $res .= $prefix . "property_filter <\n" . $this->property_filter->shortDebugString($prefix . " ") . $prefix . ">\n"; } + if (isset($this->bounding_circle_filter)) { + $res .= $prefix . "bounding_circle_filter <\n" . $this->bounding_circle_filter->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + if (isset($this->bounding_box_filter)) { + $res .= $prefix . "bounding_box_filter <\n" . $this->bounding_box_filter->shortDebugString($prefix . " ") . $prefix . ">\n"; + } return $res; } } @@ -1564,6 +1658,354 @@ namespace google\appengine\datastore\v4 { } } namespace google\appengine\datastore\v4 { + class BoundingCircleFilter extends \google\net\ProtocolMessage { + public function getProperty() { + if (!isset($this->property)) { + return new \google\appengine\datastore\v4\PropertyReference(); + } + return $this->property; + } + public function mutableProperty() { + if (!isset($this->property)) { + $res = new \google\appengine\datastore\v4\PropertyReference(); + $this->property = $res; + return $res; + } + return $this->property; + } + public function clearProperty() { + if (isset($this->property)) { + unset($this->property); + } + } + public function hasProperty() { + return isset($this->property); + } + public function getCenter() { + if (!isset($this->center)) { + return new \google\appengine\datastore\v4\GeoPoint(); + } + return $this->center; + } + public function mutableCenter() { + if (!isset($this->center)) { + $res = new \google\appengine\datastore\v4\GeoPoint(); + $this->center = $res; + return $res; + } + return $this->center; + } + public function clearCenter() { + if (isset($this->center)) { + unset($this->center); + } + } + public function hasCenter() { + return isset($this->center); + } + public function getRadiusMeters() { + if (!isset($this->radius_meters)) { + return 0.0; + } + return $this->radius_meters; + } + public function setRadiusMeters($val) { + $this->radius_meters = $val; + return $this; + } + public function clearRadiusMeters() { + unset($this->radius_meters); + return $this; + } + public function hasRadiusMeters() { + return isset($this->radius_meters); + } + public function clear() { + $this->clearProperty(); + $this->clearCenter(); + $this->clearRadiusMeters(); + } + public function byteSizePartial() { + $res = 0; + if (isset($this->property)) { + $res += 1; + $res += $this->lengthString($this->property->byteSizePartial()); + } + if (isset($this->center)) { + $res += 1; + $res += $this->lengthString($this->center->byteSizePartial()); + } + if (isset($this->radius_meters)) { + $res += 9; + } + return $res; + } + public function outputPartial($out) { + if (isset($this->property)) { + $out->putVarInt32(10); + $out->putVarInt32($this->property->byteSizePartial()); + $this->property->outputPartial($out); + } + if (isset($this->center)) { + $out->putVarInt32(18); + $out->putVarInt32($this->center->byteSizePartial()); + $this->center->outputPartial($out); + } + if (isset($this->radius_meters)) { + $out->putVarInt32(25); + $out->putDouble($this->radius_meters); + } + } + public function tryMerge($d) { + while($d->avail() > 0) { + $tt = $d->getVarInt32(); + switch ($tt) { + case 10: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableProperty()->tryMerge($tmp); + break; + case 18: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableCenter()->tryMerge($tmp); + break; + case 25: + $this->setRadiusMeters($d->getDouble()); + break; + case 0: + throw new \google\net\ProtocolBufferDecodeError(); + break; + default: + $d->skipData($tt); + } + }; + } + public function checkInitialized() { + if ((!isset($this->property)) || (!$this->property->isInitialized())) return 'property'; + if ((!isset($this->center)) || (!$this->center->isInitialized())) return 'center'; + if (!isset($this->radius_meters)) return 'radius_meters'; + return null; + } + public function mergeFrom($x) { + if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); } + if ($x->hasProperty()) { + $this->mutableProperty()->mergeFrom($x->getProperty()); + } + if ($x->hasCenter()) { + $this->mutableCenter()->mergeFrom($x->getCenter()); + } + if ($x->hasRadiusMeters()) { + $this->setRadiusMeters($x->getRadiusMeters()); + } + } + public function equals($x) { + if ($x === $this) { return true; } + if (isset($this->property) !== isset($x->property)) return false; + if (isset($this->property) && !$this->property->equals($x->property)) return false; + if (isset($this->center) !== isset($x->center)) return false; + if (isset($this->center) && !$this->center->equals($x->center)) return false; + if (isset($this->radius_meters) !== isset($x->radius_meters)) return false; + if (isset($this->radius_meters) && $this->radius_meters !== $x->radius_meters) return false; + return true; + } + public function shortDebugString($prefix = "") { + $res = ''; + if (isset($this->property)) { + $res .= $prefix . "property <\n" . $this->property->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + if (isset($this->center)) { + $res .= $prefix . "center <\n" . $this->center->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + if (isset($this->radius_meters)) { + $res .= $prefix . "radius_meters: " . $this->debugFormatDouble($this->radius_meters) . "\n"; + } + return $res; + } + } +} +namespace google\appengine\datastore\v4 { + class BoundingBoxFilter extends \google\net\ProtocolMessage { + public function getProperty() { + if (!isset($this->property)) { + return new \google\appengine\datastore\v4\PropertyReference(); + } + return $this->property; + } + public function mutableProperty() { + if (!isset($this->property)) { + $res = new \google\appengine\datastore\v4\PropertyReference(); + $this->property = $res; + return $res; + } + return $this->property; + } + public function clearProperty() { + if (isset($this->property)) { + unset($this->property); + } + } + public function hasProperty() { + return isset($this->property); + } + public function getSouthwest() { + if (!isset($this->southwest)) { + return new \google\appengine\datastore\v4\GeoPoint(); + } + return $this->southwest; + } + public function mutableSouthwest() { + if (!isset($this->southwest)) { + $res = new \google\appengine\datastore\v4\GeoPoint(); + $this->southwest = $res; + return $res; + } + return $this->southwest; + } + public function clearSouthwest() { + if (isset($this->southwest)) { + unset($this->southwest); + } + } + public function hasSouthwest() { + return isset($this->southwest); + } + public function getNortheast() { + if (!isset($this->northeast)) { + return new \google\appengine\datastore\v4\GeoPoint(); + } + return $this->northeast; + } + public function mutableNortheast() { + if (!isset($this->northeast)) { + $res = new \google\appengine\datastore\v4\GeoPoint(); + $this->northeast = $res; + return $res; + } + return $this->northeast; + } + public function clearNortheast() { + if (isset($this->northeast)) { + unset($this->northeast); + } + } + public function hasNortheast() { + return isset($this->northeast); + } + public function clear() { + $this->clearProperty(); + $this->clearSouthwest(); + $this->clearNortheast(); + } + public function byteSizePartial() { + $res = 0; + if (isset($this->property)) { + $res += 1; + $res += $this->lengthString($this->property->byteSizePartial()); + } + if (isset($this->southwest)) { + $res += 1; + $res += $this->lengthString($this->southwest->byteSizePartial()); + } + if (isset($this->northeast)) { + $res += 1; + $res += $this->lengthString($this->northeast->byteSizePartial()); + } + return $res; + } + public function outputPartial($out) { + if (isset($this->property)) { + $out->putVarInt32(10); + $out->putVarInt32($this->property->byteSizePartial()); + $this->property->outputPartial($out); + } + if (isset($this->southwest)) { + $out->putVarInt32(18); + $out->putVarInt32($this->southwest->byteSizePartial()); + $this->southwest->outputPartial($out); + } + if (isset($this->northeast)) { + $out->putVarInt32(26); + $out->putVarInt32($this->northeast->byteSizePartial()); + $this->northeast->outputPartial($out); + } + } + public function tryMerge($d) { + while($d->avail() > 0) { + $tt = $d->getVarInt32(); + switch ($tt) { + case 10: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableProperty()->tryMerge($tmp); + break; + case 18: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableSouthwest()->tryMerge($tmp); + break; + case 26: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutableNortheast()->tryMerge($tmp); + break; + case 0: + throw new \google\net\ProtocolBufferDecodeError(); + break; + default: + $d->skipData($tt); + } + }; + } + public function checkInitialized() { + if ((!isset($this->property)) || (!$this->property->isInitialized())) return 'property'; + if ((!isset($this->southwest)) || (!$this->southwest->isInitialized())) return 'southwest'; + if ((!isset($this->northeast)) || (!$this->northeast->isInitialized())) return 'northeast'; + return null; + } + public function mergeFrom($x) { + if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); } + if ($x->hasProperty()) { + $this->mutableProperty()->mergeFrom($x->getProperty()); + } + if ($x->hasSouthwest()) { + $this->mutableSouthwest()->mergeFrom($x->getSouthwest()); + } + if ($x->hasNortheast()) { + $this->mutableNortheast()->mergeFrom($x->getNortheast()); + } + } + public function equals($x) { + if ($x === $this) { return true; } + if (isset($this->property) !== isset($x->property)) return false; + if (isset($this->property) && !$this->property->equals($x->property)) return false; + if (isset($this->southwest) !== isset($x->southwest)) return false; + if (isset($this->southwest) && !$this->southwest->equals($x->southwest)) return false; + if (isset($this->northeast) !== isset($x->northeast)) return false; + if (isset($this->northeast) && !$this->northeast->equals($x->northeast)) return false; + return true; + } + public function shortDebugString($prefix = "") { + $res = ''; + if (isset($this->property)) { + $res .= $prefix . "property <\n" . $this->property->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + if (isset($this->southwest)) { + $res .= $prefix . "southwest <\n" . $this->southwest->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + if (isset($this->northeast)) { + $res .= $prefix . "northeast <\n" . $this->northeast->shortDebugString($prefix . " ") . $prefix . ">\n"; + } + return $res; + } + } +} +namespace google\appengine\datastore\v4 { class GqlQuery extends \google\net\ProtocolMessage { private $name_arg = array(); private $number_arg = array(); diff --git a/python/php/sdk/google/appengine/datastore/entity_pb.php b/python/php/sdk/google/appengine/datastore/entity_pb.php index 1022a586..1fd65665 100644 --- a/python/php/sdk/google/appengine/datastore/entity_pb.php +++ b/python/php/sdk/google/appengine/datastore/entity_pb.php @@ -2780,10 +2780,17 @@ namespace storage_onestore_v3 { } namespace storage_onestore_v3\Index\Property { class Direction { + const DIRECTION_UNSPECIFIED = 0; const ASCENDING = 1; const DESCENDING = 2; } } +namespace storage_onestore_v3\Index\Property { + class Mode { + const MODE_UNSPECIFIED = 0; + const GEOSPATIAL = 3; + } +} namespace storage_onestore_v3\Index { class Property extends \google\net\ProtocolMessage { public function getName() { @@ -2820,9 +2827,27 @@ namespace storage_onestore_v3\Index { public function hasDirection() { return isset($this->direction); } + public function getMode() { + if (!isset($this->mode)) { + return 0; + } + return $this->mode; + } + public function setMode($val) { + $this->mode = $val; + return $this; + } + public function clearMode() { + unset($this->mode); + return $this; + } + public function hasMode() { + return isset($this->mode); + } public function clear() { $this->clearName(); $this->clearDirection(); + $this->clearMode(); } public function byteSizePartial() { $res = 0; @@ -2834,6 +2859,10 @@ namespace storage_onestore_v3\Index { $res += 1; $res += $this->lengthVarInt64($this->direction); } + if (isset($this->mode)) { + $res += 1; + $res += $this->lengthVarInt64($this->mode); + } return $res; } public function outputPartial($out) { @@ -2845,6 +2874,10 @@ namespace storage_onestore_v3\Index { $out->putVarInt32(32); $out->putVarInt32($this->direction); } + if (isset($this->mode)) { + $out->putVarInt32(48); + $out->putVarInt32($this->mode); + } } public function tryMerge($d) { while($d->avail() > 0) { @@ -2859,6 +2892,9 @@ namespace storage_onestore_v3\Index { case 32: $this->setDirection($d->getVarInt32()); break; + case 48: + $this->setMode($d->getVarInt32()); + break; case 0: throw new \google\net\ProtocolBufferDecodeError(); break; @@ -2879,6 +2915,9 @@ namespace storage_onestore_v3\Index { if ($x->hasDirection()) { $this->setDirection($x->getDirection()); } + if ($x->hasMode()) { + $this->setMode($x->getMode()); + } } public function equals($x) { if ($x === $this) { return true; } @@ -2886,6 +2925,8 @@ namespace storage_onestore_v3\Index { if (isset($this->name) && $this->name !== $x->name) return false; if (isset($this->direction) !== isset($x->direction)) return false; if (isset($this->direction) && $this->direction !== $x->direction) return false; + if (isset($this->mode) !== isset($x->mode)) return false; + if (isset($this->mode) && $this->mode !== $x->mode) return false; return true; } public function shortDebugString($prefix = "") { @@ -2896,6 +2937,9 @@ namespace storage_onestore_v3\Index { if (isset($this->direction)) { $res .= $prefix . "direction: " . ($this->direction) . "\n"; } + if (isset($this->mode)) { + $res .= $prefix . "mode: " . ($this->mode) . "\n"; + } return $res; } } @@ -3085,6 +3129,7 @@ namespace storage_onestore_v3\CompositeIndex { } namespace storage_onestore_v3 { class CompositeIndex extends \google\net\ProtocolMessage { + private $read_division_family = array(); public function getAppId() { if (!isset($this->app_id)) { return ''; @@ -3179,12 +3224,51 @@ namespace storage_onestore_v3 { public function hasOnlyUseIfRequired() { return isset($this->only_use_if_required); } + public function getReadDivisionFamilySize() { + return sizeof($this->read_division_family); + } + public function getReadDivisionFamilyList() { + return $this->read_division_family; + } + public function getReadDivisionFamily($idx) { + return $this->read_division_family[$idx]; + } + public function setReadDivisionFamily($idx, $val) { + $this->read_division_family[$idx] = $val; + return $this; + } + public function addReadDivisionFamily($val) { + $this->read_division_family[] = $val; + return $this; + } + public function clearReadDivisionFamily() { + $this->read_division_family = array(); + } + public function getWriteDivisionFamily() { + if (!isset($this->write_division_family)) { + return ''; + } + return $this->write_division_family; + } + public function setWriteDivisionFamily($val) { + $this->write_division_family = $val; + return $this; + } + public function clearWriteDivisionFamily() { + unset($this->write_division_family); + return $this; + } + public function hasWriteDivisionFamily() { + return isset($this->write_division_family); + } public function clear() { $this->clearAppId(); $this->clearId(); $this->clearDefinition(); $this->clearState(); $this->clearOnlyUseIfRequired(); + $this->clearReadDivisionFamily(); + $this->clearWriteDivisionFamily(); } public function byteSizePartial() { $res = 0; @@ -3207,6 +3291,15 @@ namespace storage_onestore_v3 { if (isset($this->only_use_if_required)) { $res += 2; } + $this->checkProtoArray($this->read_division_family); + $res += 1 * sizeof($this->read_division_family); + foreach ($this->read_division_family as $value) { + $res += $this->lengthString(strlen($value)); + } + if (isset($this->write_division_family)) { + $res += 1; + $res += $this->lengthString(strlen($this->write_division_family)); + } return $res; } public function outputPartial($out) { @@ -3231,6 +3324,15 @@ namespace storage_onestore_v3 { $out->putVarInt32(48); $out->putBoolean($this->only_use_if_required); } + $this->checkProtoArray($this->read_division_family); + foreach ($this->read_division_family as $value) { + $out->putVarInt32(58); + $out->putPrefixedString($value); + } + if (isset($this->write_division_family)) { + $out->putVarInt32(66); + $out->putPrefixedString($this->write_division_family); + } } public function tryMerge($d) { while($d->avail() > 0) { @@ -3256,6 +3358,16 @@ namespace storage_onestore_v3 { case 48: $this->setOnlyUseIfRequired($d->getBoolean()); break; + case 58: + $length = $d->getVarInt32(); + $this->addReadDivisionFamily(substr($d->buffer(), $d->pos(), $length)); + $d->skip($length); + break; + case 66: + $length = $d->getVarInt32(); + $this->setWriteDivisionFamily(substr($d->buffer(), $d->pos(), $length)); + $d->skip($length); + break; case 0: throw new \google\net\ProtocolBufferDecodeError(); break; @@ -3288,6 +3400,12 @@ namespace storage_onestore_v3 { if ($x->hasOnlyUseIfRequired()) { $this->setOnlyUseIfRequired($x->getOnlyUseIfRequired()); } + foreach ($x->getReadDivisionFamilyList() as $v) { + $this->addReadDivisionFamily($v); + } + if ($x->hasWriteDivisionFamily()) { + $this->setWriteDivisionFamily($x->getWriteDivisionFamily()); + } } public function equals($x) { if ($x === $this) { return true; } @@ -3301,6 +3419,12 @@ namespace storage_onestore_v3 { if (isset($this->state) && $this->state !== $x->state) return false; if (isset($this->only_use_if_required) !== isset($x->only_use_if_required)) return false; if (isset($this->only_use_if_required) && $this->only_use_if_required !== $x->only_use_if_required) return false; + if (sizeof($this->read_division_family) !== sizeof($x->read_division_family)) return false; + foreach (array_map(null, $this->read_division_family, $x->read_division_family) as $v) { + if ($v[0] !== $v[1]) return false; + } + if (isset($this->write_division_family) !== isset($x->write_division_family)) return false; + if (isset($this->write_division_family) && $this->write_division_family !== $x->write_division_family) return false; return true; } public function shortDebugString($prefix = "") { @@ -3320,6 +3444,346 @@ namespace storage_onestore_v3 { if (isset($this->only_use_if_required)) { $res .= $prefix . "only_use_if_required: " . $this->debugFormatBool($this->only_use_if_required) . "\n"; } + foreach ($this->read_division_family as $value) { + $res .= $prefix . "read_division_family: " . $this->debugFormatString($value) . "\n"; + } + if (isset($this->write_division_family)) { + $res .= $prefix . "write_division_family: " . $this->debugFormatString($this->write_division_family) . "\n"; + } + return $res; + } + } +} +namespace storage_onestore_v3 { + class SearchIndexEntry extends \google\net\ProtocolMessage { + private $division_family = array(); + public function getIndexId() { + if (!isset($this->index_id)) { + return "0"; + } + return $this->index_id; + } + public function setIndexId($val) { + if (is_double($val)) { + $this->index_id = sprintf('%0.0F', $val); + } else { + $this->index_id = $val; + } + return $this; + } + public function clearIndexId() { + unset($this->index_id); + return $this; + } + public function hasIndexId() { + return isset($this->index_id); + } + public function getDivisionFamilySize() { + return sizeof($this->division_family); + } + public function getDivisionFamilyList() { + return $this->division_family; + } + public function getDivisionFamily($idx) { + return $this->division_family[$idx]; + } + public function setDivisionFamily($idx, $val) { + $this->division_family[$idx] = $val; + return $this; + } + public function addDivisionFamily($val) { + $this->division_family[] = $val; + return $this; + } + public function clearDivisionFamily() { + $this->division_family = array(); + } + public function getFingerprint1999() { + if (!isset($this->fingerprint_1999)) { + return "0"; + } + return $this->fingerprint_1999; + } + public function setFingerprint1999($val) { + if (is_double($val)) { + $this->fingerprint_1999 = sprintf('%0.0F', $val); + } else { + $this->fingerprint_1999 = $val; + } + return $this; + } + public function clearFingerprint1999() { + unset($this->fingerprint_1999); + return $this; + } + public function hasFingerprint1999() { + return isset($this->fingerprint_1999); + } + public function getFingerprint2011() { + if (!isset($this->fingerprint_2011)) { + return "0"; + } + return $this->fingerprint_2011; + } + public function setFingerprint2011($val) { + if (is_double($val)) { + $this->fingerprint_2011 = sprintf('%0.0F', $val); + } else { + $this->fingerprint_2011 = $val; + } + return $this; + } + public function clearFingerprint2011() { + unset($this->fingerprint_2011); + return $this; + } + public function hasFingerprint2011() { + return isset($this->fingerprint_2011); + } + public function clear() { + $this->clearIndexId(); + $this->clearDivisionFamily(); + $this->clearFingerprint1999(); + $this->clearFingerprint2011(); + } + public function byteSizePartial() { + $res = 0; + if (isset($this->index_id)) { + $res += 1; + $res += $this->lengthVarInt64($this->index_id); + } + $this->checkProtoArray($this->division_family); + $res += 1 * sizeof($this->division_family); + foreach ($this->division_family as $value) { + $res += $this->lengthString(strlen($value)); + } + if (isset($this->fingerprint_1999)) { + $res += 9; + } + if (isset($this->fingerprint_2011)) { + $res += 9; + } + return $res; + } + public function outputPartial($out) { + if (isset($this->index_id)) { + $out->putVarInt32(8); + $out->putVarInt64($this->index_id); + } + $this->checkProtoArray($this->division_family); + foreach ($this->division_family as $value) { + $out->putVarInt32(18); + $out->putPrefixedString($value); + } + if (isset($this->fingerprint_1999)) { + $out->putVarInt32(25); + $out->put64($this->fingerprint_1999); + } + if (isset($this->fingerprint_2011)) { + $out->putVarInt32(33); + $out->put64($this->fingerprint_2011); + } + } + public function tryMerge($d) { + while($d->avail() > 0) { + $tt = $d->getVarInt32(); + switch ($tt) { + case 8: + $this->setIndexId($d->getVarInt64()); + break; + case 18: + $length = $d->getVarInt32(); + $this->addDivisionFamily(substr($d->buffer(), $d->pos(), $length)); + $d->skip($length); + break; + case 25: + $this->setFingerprint1999($d->getFixed64()); + break; + case 33: + $this->setFingerprint2011($d->getFixed64()); + break; + case 0: + throw new \google\net\ProtocolBufferDecodeError(); + break; + default: + $d->skipData($tt); + } + }; + } + public function checkInitialized() { + if (!isset($this->index_id)) return 'index_id'; + return null; + } + public function mergeFrom($x) { + if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); } + if ($x->hasIndexId()) { + $this->setIndexId($x->getIndexId()); + } + foreach ($x->getDivisionFamilyList() as $v) { + $this->addDivisionFamily($v); + } + if ($x->hasFingerprint1999()) { + $this->setFingerprint1999($x->getFingerprint1999()); + } + if ($x->hasFingerprint2011()) { + $this->setFingerprint2011($x->getFingerprint2011()); + } + } + public function equals($x) { + if ($x === $this) { return true; } + if (isset($this->index_id) !== isset($x->index_id)) return false; + if (isset($this->index_id) && !$this->integerEquals($this->index_id, $x->index_id)) return false; + if (sizeof($this->division_family) !== sizeof($x->division_family)) return false; + foreach (array_map(null, $this->division_family, $x->division_family) as $v) { + if ($v[0] !== $v[1]) return false; + } + if (isset($this->fingerprint_1999) !== isset($x->fingerprint_1999)) return false; + if (isset($this->fingerprint_1999) && !$this->integerEquals($this->fingerprint_1999, $x->fingerprint_1999)) return false; + if (isset($this->fingerprint_2011) !== isset($x->fingerprint_2011)) return false; + if (isset($this->fingerprint_2011) && !$this->integerEquals($this->fingerprint_2011, $x->fingerprint_2011)) return false; + return true; + } + public function shortDebugString($prefix = "") { + $res = ''; + if (isset($this->index_id)) { + $res .= $prefix . "index_id: " . $this->debugFormatInt64($this->index_id) . "\n"; + } + foreach ($this->division_family as $value) { + $res .= $prefix . "division_family: " . $this->debugFormatString($value) . "\n"; + } + if (isset($this->fingerprint_1999)) { + $res .= $prefix . "fingerprint_1999: " . $this->debugFormatFixed64($this->fingerprint_1999) . "\n"; + } + if (isset($this->fingerprint_2011)) { + $res .= $prefix . "fingerprint_2011: " . $this->debugFormatFixed64($this->fingerprint_2011) . "\n"; + } + return $res; + } + } +} +namespace storage_onestore_v3 { + class SearchIndexExternalId extends \google\net\ProtocolMessage { + public function getIndexId() { + if (!isset($this->index_id)) { + return "0"; + } + return $this->index_id; + } + public function setIndexId($val) { + if (is_double($val)) { + $this->index_id = sprintf('%0.0F', $val); + } else { + $this->index_id = $val; + } + return $this; + } + public function clearIndexId() { + unset($this->index_id); + return $this; + } + public function hasIndexId() { + return isset($this->index_id); + } + public function getPrimaryKey() { + if (!isset($this->primary_key)) { + return new \storage_onestore_v3\Reference(); + } + return $this->primary_key; + } + public function mutablePrimaryKey() { + if (!isset($this->primary_key)) { + $res = new \storage_onestore_v3\Reference(); + $this->primary_key = $res; + return $res; + } + return $this->primary_key; + } + public function clearPrimaryKey() { + if (isset($this->primary_key)) { + unset($this->primary_key); + } + } + public function hasPrimaryKey() { + return isset($this->primary_key); + } + public function clear() { + $this->clearIndexId(); + $this->clearPrimaryKey(); + } + public function byteSizePartial() { + $res = 0; + if (isset($this->index_id)) { + $res += 1; + $res += $this->lengthVarInt64($this->index_id); + } + if (isset($this->primary_key)) { + $res += 1; + $res += $this->lengthString($this->primary_key->byteSizePartial()); + } + return $res; + } + public function outputPartial($out) { + if (isset($this->index_id)) { + $out->putVarInt32(8); + $out->putVarInt64($this->index_id); + } + if (isset($this->primary_key)) { + $out->putVarInt32(18); + $out->putVarInt32($this->primary_key->byteSizePartial()); + $this->primary_key->outputPartial($out); + } + } + public function tryMerge($d) { + while($d->avail() > 0) { + $tt = $d->getVarInt32(); + switch ($tt) { + case 8: + $this->setIndexId($d->getVarInt64()); + break; + case 18: + $length = $d->getVarInt32(); + $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length); + $d->skip($length); + $this->mutablePrimaryKey()->tryMerge($tmp); + break; + case 0: + throw new \google\net\ProtocolBufferDecodeError(); + break; + default: + $d->skipData($tt); + } + }; + } + public function checkInitialized() { + if (!isset($this->index_id)) return 'index_id'; + if ((!isset($this->primary_key)) || (!$this->primary_key->isInitialized())) return 'primary_key'; + return null; + } + public function mergeFrom($x) { + if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); } + if ($x->hasIndexId()) { + $this->setIndexId($x->getIndexId()); + } + if ($x->hasPrimaryKey()) { + $this->mutablePrimaryKey()->mergeFrom($x->getPrimaryKey()); + } + } + public function equals($x) { + if ($x === $this) { return true; } + if (isset($this->index_id) !== isset($x->index_id)) return false; + if (isset($this->index_id) && !$this->integerEquals($this->index_id, $x->index_id)) return false; + if (isset($this->primary_key) !== isset($x->primary_key)) return false; + if (isset($this->primary_key) && !$this->primary_key->equals($x->primary_key)) return false; + return true; + } + public function shortDebugString($prefix = "") { + $res = ''; + if (isset($this->index_id)) { + $res .= $prefix . "index_id: " . $this->debugFormatInt64($this->index_id) . "\n"; + } + if (isset($this->primary_key)) { + $res .= $prefix . "primary_key <\n" . $this->primary_key->shortDebugString($prefix . " ") . $prefix . ">\n"; + } return $res; } } diff --git a/python/php/sdk/google/appengine/runtime/autoloader.php b/python/php/sdk/google/appengine/runtime/autoloader.php index c06c318e..c120ddbe 100644 --- a/python/php/sdk/google/appengine/runtime/autoloader.php +++ b/python/php/sdk/google/appengine/runtime/autoloader.php @@ -90,10 +90,13 @@ final class ClassLoader { 'storage_onestore_v3\entityproto' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\compositeproperty' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\index\property\direction' => 'google/appengine/datastore/entity_pb.php', + 'storage_onestore_v3\index\property\mode' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\index\property' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\index' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\compositeindex\state' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\compositeindex' => 'google/appengine/datastore/entity_pb.php', + 'storage_onestore_v3\searchindexentry' => 'google/appengine/datastore/entity_pb.php', + 'storage_onestore_v3\searchindexexternalid' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\indexpostfix\indexvalue' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\indexpostfix' => 'google/appengine/datastore/entity_pb.php', 'storage_onestore_v3\indexposition' => 'google/appengine/datastore/entity_pb.php', @@ -113,6 +116,8 @@ final class ClassLoader { 'google\appengine\datastore\v4\compositefilter' => 'google/appengine/datastore/datastore_v4_pb.php', 'google\appengine\datastore\v4\propertyfilter\operator' => 'google/appengine/datastore/datastore_v4_pb.php', 'google\appengine\datastore\v4\propertyfilter' => 'google/appengine/datastore/datastore_v4_pb.php', + 'google\appengine\datastore\v4\boundingcirclefilter' => 'google/appengine/datastore/datastore_v4_pb.php', + 'google\appengine\datastore\v4\boundingboxfilter' => 'google/appengine/datastore/datastore_v4_pb.php', 'google\appengine\datastore\v4\gqlquery' => 'google/appengine/datastore/datastore_v4_pb.php', 'google\appengine\datastore\v4\gqlqueryarg' => 'google/appengine/datastore/datastore_v4_pb.php', 'google\appengine\datastore\v4\queryresultbatch\moreresultstype' => 'google/appengine/datastore/datastore_v4_pb.php', diff --git a/python/wrapper_util.py b/python/wrapper_util.py index 7ba646ad..70e0bada 100644 --- a/python/wrapper_util.py +++ b/python/wrapper_util.py @@ -152,7 +152,7 @@ class Paths(object): self.google_sql_extra_paths = self.oauth_client_extra_paths + [ - os.path.join(dir_path, 'lib', 'enum'), + os.path.join(dir_path, 'lib', 'deprecated_enum'), os.path.join(dir_path, 'lib', 'grizzled'), os.path.join(dir_path, 'lib', 'oauth2'), os.path.join(dir_path, 'lib', 'prettytable'), -- 2.11.4.GIT