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