OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2015 Google Inc. |
| 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. |
| 16 |
| 17 """Simple tool for generating a client library. |
| 18 |
| 19 Relevant links: |
| 20 https://developers.google.com/discovery/v1/reference/apis#resource |
| 21 """ |
| 22 |
| 23 import datetime |
| 24 |
| 25 from six.moves import urllib_parse |
| 26 |
| 27 from apitools.gen import command_registry |
| 28 from apitools.gen import message_registry |
| 29 from apitools.gen import service_registry |
| 30 from apitools.gen import util |
| 31 |
| 32 |
| 33 def _ApitoolsVersion(): |
| 34 """Returns version of the currently installed google-apitools package.""" |
| 35 import pkg_resources |
| 36 return pkg_resources.get_distribution('google-apitools').version |
| 37 |
| 38 |
| 39 def _StandardQueryParametersSchema(discovery_doc): |
| 40 """Sets up dict of standard query parameters.""" |
| 41 standard_query_schema = { |
| 42 'id': 'StandardQueryParameters', |
| 43 'type': 'object', |
| 44 'description': 'Query parameters accepted by all methods.', |
| 45 'properties': discovery_doc.get('parameters', {}), |
| 46 } |
| 47 # We add an entry for the trace, since Discovery doesn't. |
| 48 standard_query_schema['properties']['trace'] = { |
| 49 'type': 'string', |
| 50 'description': ('A tracing token of the form "token:<tokenid>" ' |
| 51 'to include in api requests.'), |
| 52 'location': 'query', |
| 53 } |
| 54 return standard_query_schema |
| 55 |
| 56 |
| 57 def _ComputePaths(package, version, discovery_doc): |
| 58 full_path = urllib_parse.urljoin( |
| 59 discovery_doc['rootUrl'], discovery_doc['servicePath']) |
| 60 api_path_component = '/'.join((package, version, '')) |
| 61 if api_path_component not in full_path: |
| 62 return full_path, '' |
| 63 prefix, _, suffix = full_path.rpartition(api_path_component) |
| 64 return prefix + api_path_component, suffix |
| 65 |
| 66 |
| 67 class DescriptorGenerator(object): |
| 68 |
| 69 """Code generator for a given discovery document.""" |
| 70 |
| 71 def __init__(self, discovery_doc, client_info, names, root_package, outdir, |
| 72 base_package, protorpc_package, generate_cli=False, |
| 73 use_proto2=False, unelidable_request_methods=None, |
| 74 apitools_version=''): |
| 75 self.__discovery_doc = discovery_doc |
| 76 self.__client_info = client_info |
| 77 self.__outdir = outdir |
| 78 self.__use_proto2 = use_proto2 |
| 79 self.__description = util.CleanDescription( |
| 80 self.__discovery_doc.get('description', '')) |
| 81 self.__package = self.__client_info.package |
| 82 self.__version = self.__client_info.version |
| 83 self.__revision = discovery_doc.get('revision', '1') |
| 84 self.__generate_cli = generate_cli |
| 85 self.__root_package = root_package |
| 86 self.__base_files_package = base_package |
| 87 self.__protorpc_package = protorpc_package |
| 88 self.__names = names |
| 89 self.__base_url, self.__base_path = _ComputePaths( |
| 90 self.__package, self.__client_info.url_version, |
| 91 self.__discovery_doc) |
| 92 |
| 93 # Order is important here: we need the schemas before we can |
| 94 # define the services. |
| 95 self.__message_registry = message_registry.MessageRegistry( |
| 96 self.__client_info, self.__names, self.__description, |
| 97 self.__root_package, self.__base_files_package, |
| 98 self.__protorpc_package) |
| 99 schemas = self.__discovery_doc.get('schemas', {}) |
| 100 for schema_name, schema in schemas.items(): |
| 101 self.__message_registry.AddDescriptorFromSchema( |
| 102 schema_name, schema) |
| 103 |
| 104 # We need to add one more message type for the global parameters. |
| 105 standard_query_schema = _StandardQueryParametersSchema( |
| 106 self.__discovery_doc) |
| 107 self.__message_registry.AddDescriptorFromSchema( |
| 108 standard_query_schema['id'], standard_query_schema) |
| 109 |
| 110 # Now that we know all the messages, we need to correct some |
| 111 # fields from MessageFields to EnumFields. |
| 112 self.__message_registry.FixupMessageFields() |
| 113 |
| 114 self.__command_registry = command_registry.CommandRegistry( |
| 115 self.__package, self.__version, self.__client_info, |
| 116 self.__message_registry, self.__root_package, |
| 117 self.__base_files_package, self.__protorpc_package, |
| 118 self.__base_url, self.__names) |
| 119 self.__command_registry.AddGlobalParameters( |
| 120 self.__message_registry.LookupDescriptorOrDie( |
| 121 'StandardQueryParameters')) |
| 122 |
| 123 self.__services_registry = service_registry.ServiceRegistry( |
| 124 self.__client_info, |
| 125 self.__message_registry, |
| 126 self.__command_registry, |
| 127 self.__base_url, |
| 128 self.__base_path, |
| 129 self.__names, |
| 130 self.__root_package, |
| 131 self.__base_files_package, |
| 132 unelidable_request_methods or []) |
| 133 services = self.__discovery_doc.get('resources', {}) |
| 134 for service_name, methods in sorted(services.items()): |
| 135 self.__services_registry.AddServiceFromResource( |
| 136 service_name, methods) |
| 137 # We might also have top-level methods. |
| 138 api_methods = self.__discovery_doc.get('methods', []) |
| 139 if api_methods: |
| 140 self.__services_registry.AddServiceFromResource( |
| 141 'api', {'methods': api_methods}) |
| 142 # pylint: disable=protected-access |
| 143 self.__client_info = self.__client_info._replace( |
| 144 scopes=self.__services_registry.scopes) |
| 145 |
| 146 # The apitools version that will be used in prerequisites for the |
| 147 # generated packages. |
| 148 self.__apitools_version = ( |
| 149 apitools_version if apitools_version else _ApitoolsVersion()) |
| 150 |
| 151 @property |
| 152 def client_info(self): |
| 153 return self.__client_info |
| 154 |
| 155 @property |
| 156 def discovery_doc(self): |
| 157 return self.__discovery_doc |
| 158 |
| 159 @property |
| 160 def names(self): |
| 161 return self.__names |
| 162 |
| 163 @property |
| 164 def outdir(self): |
| 165 return self.__outdir |
| 166 |
| 167 @property |
| 168 def package(self): |
| 169 return self.__package |
| 170 |
| 171 @property |
| 172 def use_proto2(self): |
| 173 return self.__use_proto2 |
| 174 |
| 175 @property |
| 176 def apitools_version(self): |
| 177 return self.__apitools_version |
| 178 |
| 179 def _GetPrinter(self, out): |
| 180 printer = util.SimplePrettyPrinter(out) |
| 181 return printer |
| 182 |
| 183 def WriteInit(self, out): |
| 184 """Write a simple __init__.py for the generated client.""" |
| 185 printer = self._GetPrinter(out) |
| 186 printer('"""Common imports for generated %s client library."""', |
| 187 self.__client_info.package) |
| 188 printer('# pylint:disable=wildcard-import') |
| 189 printer() |
| 190 printer('import pkgutil') |
| 191 printer() |
| 192 printer('from %s import *', self.__base_files_package) |
| 193 if self.__root_package == '.': |
| 194 import_prefix = '' |
| 195 else: |
| 196 import_prefix = '%s.' % self.__root_package |
| 197 if self.__generate_cli: |
| 198 printer('from %s%s import *', |
| 199 import_prefix, self.__client_info.cli_rule_name) |
| 200 printer('from %s%s import *', |
| 201 import_prefix, self.__client_info.client_rule_name) |
| 202 printer('from %s%s import *', |
| 203 import_prefix, self.__client_info.messages_rule_name) |
| 204 printer() |
| 205 printer('__path__ = pkgutil.extend_path(__path__, __name__)') |
| 206 |
| 207 def WriteIntermediateInit(self, out): |
| 208 """Write a simple __init__.py for an intermediate directory.""" |
| 209 printer = self._GetPrinter(out) |
| 210 printer('#!/usr/bin/env python') |
| 211 printer('"""Shared __init__.py for apitools."""') |
| 212 printer() |
| 213 printer('from pkgutil import extend_path') |
| 214 printer('__path__ = extend_path(__path__, __name__)') |
| 215 |
| 216 def WriteSetupPy(self, out): |
| 217 """Write a setup.py for upload to PyPI.""" |
| 218 printer = self._GetPrinter(out) |
| 219 year = datetime.datetime.now().year |
| 220 printer('# Copyright %s Google Inc. All Rights Reserved.' % year) |
| 221 printer('#') |
| 222 printer('# Licensed under the Apache License, Version 2.0 (the' |
| 223 '"License");') |
| 224 printer('# you may not use this file except in compliance with ' |
| 225 'the License.') |
| 226 printer('# You may obtain a copy of the License at') |
| 227 printer('#') |
| 228 printer('# http://www.apache.org/licenses/LICENSE-2.0') |
| 229 printer('#') |
| 230 printer('# Unless required by applicable law or agreed to in writing, ' |
| 231 'software') |
| 232 printer('# distributed under the License is distributed on an "AS IS" ' |
| 233 'BASIS,') |
| 234 printer('# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ' |
| 235 'express or implied.') |
| 236 printer('# See the License for the specific language governing ' |
| 237 'permissions and') |
| 238 printer('# limitations under the License.') |
| 239 printer() |
| 240 printer('import setuptools') |
| 241 printer('REQUIREMENTS = [') |
| 242 with printer.Indent(indent=' '): |
| 243 if self.apitools_version.startswith('0.4.'): |
| 244 printer('"google-apitools>=0.4.8,<0.5",') |
| 245 else: |
| 246 printer('"google-apitools==%s",', self.apitools_version) |
| 247 printer('"httplib2>=0.9",') |
| 248 printer('"oauth2client>=1.4.12",') |
| 249 printer(']') |
| 250 printer('_PACKAGE = "apitools.clients.%s"' % self.__package) |
| 251 printer() |
| 252 printer('setuptools.setup(') |
| 253 # TODO(craigcitro): Allow customization of these options. |
| 254 with printer.Indent(indent=' '): |
| 255 printer('name="google-apitools-%s-%s",', |
| 256 self.__package, self.__version) |
| 257 if self.apitools_version.startswith('0.4.'): |
| 258 printer('version="0.4.%s",', self.__revision) |
| 259 else: |
| 260 printer('version="%s.%s",', |
| 261 self.apitools_version, self.__revision) |
| 262 printer('description="Autogenerated apitools library for %s",' % ( |
| 263 self.__package,)) |
| 264 printer('url="https://github.com/google/apitools",') |
| 265 printer('author="Craig Citro",') |
| 266 printer('author_email="craigcitro@google.com",') |
| 267 printer('packages=setuptools.find_packages(),') |
| 268 printer('install_requires=REQUIREMENTS,') |
| 269 printer('classifiers=[') |
| 270 with printer.Indent(indent=' '): |
| 271 printer('"Programming Language :: Python :: 2.7",') |
| 272 printer('"License :: OSI Approved :: Apache Software ' |
| 273 'License",') |
| 274 printer('],') |
| 275 printer('license="Apache 2.0",') |
| 276 printer('keywords="apitools apitools-%s %s",' % ( |
| 277 self.__package, self.__package)) |
| 278 printer(')') |
| 279 |
| 280 def WriteMessagesFile(self, out): |
| 281 self.__message_registry.WriteFile(self._GetPrinter(out)) |
| 282 |
| 283 def WriteMessagesProtoFile(self, out): |
| 284 self.__message_registry.WriteProtoFile(self._GetPrinter(out)) |
| 285 |
| 286 def WriteServicesProtoFile(self, out): |
| 287 self.__services_registry.WriteProtoFile(self._GetPrinter(out)) |
| 288 |
| 289 def WriteClientLibrary(self, out): |
| 290 self.__services_registry.WriteFile(self._GetPrinter(out)) |
| 291 |
| 292 def WriteCli(self, out): |
| 293 self.__command_registry.WriteFile(self._GetPrinter(out)) |
OLD | NEW |