• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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('from __future__ import absolute_import')
172        printer()
173        printer('import pkgutil')
174        printer()
175        if self.__init_wildcards_file:
176            printer('from %s import *', self.__base_files_package)
177            if self.__root_package == '.':
178                import_prefix = '.'
179            else:
180                import_prefix = '%s.' % self.__root_package
181            printer('from %s%s import *',
182                    import_prefix, self.__client_info.client_rule_name)
183            printer('from %s%s import *',
184                    import_prefix, self.__client_info.messages_rule_name)
185            printer()
186        printer('__path__ = pkgutil.extend_path(__path__, __name__)')
187
188    def WriteIntermediateInit(self, out):
189        """Write a simple __init__.py for an intermediate directory."""
190        printer = self._GetPrinter(out)
191        printer('#!/usr/bin/env python')
192        printer('"""Shared __init__.py for apitools."""')
193        printer()
194        printer('from pkgutil import extend_path')
195        printer('__path__ = extend_path(__path__, __name__)')
196
197    def WriteSetupPy(self, out):
198        """Write a setup.py for upload to PyPI."""
199        printer = self._GetPrinter(out)
200        year = datetime.datetime.now().year
201        printer('# Copyright %s Google Inc. All Rights Reserved.' % year)
202        printer('#')
203        printer('# Licensed under the Apache License, Version 2.0 (the'
204                '"License");')
205        printer('# you may not use this file except in compliance with '
206                'the License.')
207        printer('# You may obtain a copy of the License at')
208        printer('#')
209        printer('#   http://www.apache.org/licenses/LICENSE-2.0')
210        printer('#')
211        printer('# Unless required by applicable law or agreed to in writing, '
212                'software')
213        printer('# distributed under the License is distributed on an "AS IS" '
214                'BASIS,')
215        printer('# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either '
216                'express or implied.')
217        printer('# See the License for the specific language governing '
218                'permissions and')
219        printer('# limitations under the License.')
220        printer()
221        printer('import setuptools')
222        printer('REQUIREMENTS = [')
223        with printer.Indent(indent='    '):
224            parts = self.apitools_version.split('.')
225            major = parts.pop(0)
226            minor = parts.pop(0)
227            printer('"google-apitools>=%s,~=%s.%s",',
228                    self.apitools_version, major, minor)
229            printer('"httplib2>=0.9",')
230            printer('"oauth2client>=1.4.12",')
231        printer(']')
232        printer('_PACKAGE = "apitools.clients.%s"' % self.__package)
233        printer()
234        printer('setuptools.setup(')
235        # TODO(craigcitro): Allow customization of these options.
236        with printer.Indent(indent='    '):
237            printer('name="google-apitools-%s-%s",',
238                    self.__package, self.__version)
239            printer('version="%s.%s",',
240                    self.apitools_version, self.__revision)
241            printer('description="Autogenerated apitools library for %s",' % (
242                self.__package,))
243            printer('url="https://github.com/google/apitools",')
244            printer('author="Craig Citro",')
245            printer('author_email="craigcitro@google.com",')
246            printer('packages=setuptools.find_packages(),')
247            printer('install_requires=REQUIREMENTS,')
248            printer('classifiers=[')
249            with printer.Indent(indent='    '):
250                printer('"Programming Language :: Python :: 2.7",')
251                printer('"License :: OSI Approved :: Apache Software '
252                        'License",')
253            printer('],')
254            printer('license="Apache 2.0",')
255            printer('keywords="apitools apitools-%s %s",' % (
256                self.__package, self.__package))
257        printer(')')
258
259    def WriteMessagesFile(self, out):
260        self.__message_registry.WriteFile(self._GetPrinter(out))
261
262    def WriteMessagesProtoFile(self, out):
263        self.__message_registry.WriteProtoFile(self._GetPrinter(out))
264
265    def WriteServicesProtoFile(self, out):
266        self.__services_registry.WriteProtoFile(self._GetPrinter(out))
267
268    def WriteClientLibrary(self, out):
269        self.__services_registry.WriteFile(self._GetPrinter(out))
270