Index: third_party/google_api_python_client/describe.py |
diff --git a/third_party/google_api_python_client/describe.py b/third_party/google_api_python_client/describe.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..5dcac904c61a559b6e78003d63576bfdb1759b15 |
--- /dev/null |
+++ b/third_party/google_api_python_client/describe.py |
@@ -0,0 +1,390 @@ |
+#!/usr/bin/python |
+# |
+# Copyright 2014 Google Inc. All Rights Reserved. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""Create documentation for generate API surfaces. |
+ |
+Command-line tool that creates documentation for all APIs listed in discovery. |
+The documentation is generated from a combination of the discovery document and |
+the generated API surface itself. |
+""" |
+ |
+__author__ = 'jcgregorio@google.com (Joe Gregorio)' |
+ |
+import argparse |
+import json |
+import os |
+import re |
+import string |
+import sys |
+ |
+from googleapiclient.discovery import DISCOVERY_URI |
+from googleapiclient.discovery import build |
+from googleapiclient.discovery import build_from_document |
+import httplib2 |
+import uritemplate |
+ |
+CSS = """<style> |
+ |
+body, h1, h2, h3, div, span, p, pre, a { |
+ margin: 0; |
+ padding: 0; |
+ border: 0; |
+ font-weight: inherit; |
+ font-style: inherit; |
+ font-size: 100%; |
+ font-family: inherit; |
+ vertical-align: baseline; |
+} |
+ |
+body { |
+ font-size: 13px; |
+ padding: 1em; |
+} |
+ |
+h1 { |
+ font-size: 26px; |
+ margin-bottom: 1em; |
+} |
+ |
+h2 { |
+ font-size: 24px; |
+ margin-bottom: 1em; |
+} |
+ |
+h3 { |
+ font-size: 20px; |
+ margin-bottom: 1em; |
+ margin-top: 1em; |
+} |
+ |
+pre, code { |
+ line-height: 1.5; |
+ font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; |
+} |
+ |
+pre { |
+ margin-top: 0.5em; |
+} |
+ |
+h1, h2, h3, p { |
+ font-family: Arial, sans serif; |
+} |
+ |
+h1, h2, h3 { |
+ border-bottom: solid #CCC 1px; |
+} |
+ |
+.toc_element { |
+ margin-top: 0.5em; |
+} |
+ |
+.firstline { |
+ margin-left: 2 em; |
+} |
+ |
+.method { |
+ margin-top: 1em; |
+ border: solid 1px #CCC; |
+ padding: 1em; |
+ background: #EEE; |
+} |
+ |
+.details { |
+ font-weight: bold; |
+ font-size: 14px; |
+} |
+ |
+</style> |
+""" |
+ |
+METHOD_TEMPLATE = """<div class="method"> |
+ <code class="details" id="$name">$name($params)</code> |
+ <pre>$doc</pre> |
+</div> |
+""" |
+ |
+COLLECTION_LINK = """<p class="toc_element"> |
+ <code><a href="$href">$name()</a></code> |
+</p> |
+<p class="firstline">Returns the $name Resource.</p> |
+""" |
+ |
+METHOD_LINK = """<p class="toc_element"> |
+ <code><a href="#$name">$name($params)</a></code></p> |
+<p class="firstline">$firstline</p>""" |
+ |
+BASE = 'docs/dyn' |
+ |
+DIRECTORY_URI = 'https://www.googleapis.com/discovery/v1/apis?preferred=true' |
+ |
+parser = argparse.ArgumentParser(description=__doc__) |
+ |
+parser.add_argument('--discovery_uri_template', default=DISCOVERY_URI, |
+ help='URI Template for discovery.') |
+ |
+parser.add_argument('--discovery_uri', default='', |
+ help=('URI of discovery document. If supplied then only ' |
+ 'this API will be documented.')) |
+ |
+parser.add_argument('--directory_uri', default=DIRECTORY_URI, |
+ help=('URI of directory document. Unused if --discovery_uri' |
+ ' is supplied.')) |
+ |
+parser.add_argument('--dest', default=BASE, |
+ help='Directory name to write documents into.') |
+ |
+ |
+ |
+def safe_version(version): |
+ """Create a safe version of the verion string. |
+ |
+ Needed so that we can distinguish between versions |
+ and sub-collections in URIs. I.e. we don't want |
+ adsense_v1.1 to refer to the '1' collection in the v1 |
+ version of the adsense api. |
+ |
+ Args: |
+ version: string, The version string. |
+ Returns: |
+ The string with '.' replaced with '_'. |
+ """ |
+ |
+ return version.replace('.', '_') |
+ |
+ |
+def unsafe_version(version): |
+ """Undoes what safe_version() does. |
+ |
+ See safe_version() for the details. |
+ |
+ |
+ Args: |
+ version: string, The safe version string. |
+ Returns: |
+ The string with '_' replaced with '.'. |
+ """ |
+ |
+ return version.replace('_', '.') |
+ |
+ |
+def method_params(doc): |
+ """Document the parameters of a method. |
+ |
+ Args: |
+ doc: string, The method's docstring. |
+ |
+ Returns: |
+ The method signature as a string. |
+ """ |
+ doclines = doc.splitlines() |
+ if 'Args:' in doclines: |
+ begin = doclines.index('Args:') |
+ if 'Returns:' in doclines[begin+1:]: |
+ end = doclines.index('Returns:', begin) |
+ args = doclines[begin+1: end] |
+ else: |
+ args = doclines[begin+1:] |
+ |
+ parameters = [] |
+ for line in args: |
+ m = re.search('^\s+([a-zA-Z0-9_]+): (.*)', line) |
+ if m is None: |
+ continue |
+ pname = m.group(1) |
+ desc = m.group(2) |
+ if '(required)' not in desc: |
+ pname = pname + '=None' |
+ parameters.append(pname) |
+ parameters = ', '.join(parameters) |
+ else: |
+ parameters = '' |
+ return parameters |
+ |
+ |
+def method(name, doc): |
+ """Documents an individual method. |
+ |
+ Args: |
+ name: string, Name of the method. |
+ doc: string, The methods docstring. |
+ """ |
+ |
+ params = method_params(doc) |
+ return string.Template(METHOD_TEMPLATE).substitute( |
+ name=name, params=params, doc=doc) |
+ |
+ |
+def breadcrumbs(path, root_discovery): |
+ """Create the breadcrumb trail to this page of documentation. |
+ |
+ Args: |
+ path: string, Dot separated name of the resource. |
+ root_discovery: Deserialized discovery document. |
+ |
+ Returns: |
+ HTML with links to each of the parent resources of this resource. |
+ """ |
+ parts = path.split('.') |
+ |
+ crumbs = [] |
+ accumulated = [] |
+ |
+ for i, p in enumerate(parts): |
+ prefix = '.'.join(accumulated) |
+ # The first time through prefix will be [], so we avoid adding in a |
+ # superfluous '.' to prefix. |
+ if prefix: |
+ prefix += '.' |
+ display = p |
+ if i == 0: |
+ display = root_discovery.get('title', display) |
+ crumbs.append('<a href="%s.html">%s</a>' % (prefix + p, display)) |
+ accumulated.append(p) |
+ |
+ return ' . '.join(crumbs) |
+ |
+ |
+def document_collection(resource, path, root_discovery, discovery, css=CSS): |
+ """Document a single collection in an API. |
+ |
+ Args: |
+ resource: Collection or service being documented. |
+ path: string, Dot separated name of the resource. |
+ root_discovery: Deserialized discovery document. |
+ discovery: Deserialized discovery document, but just the portion that |
+ describes the resource. |
+ css: string, The CSS to include in the generated file. |
+ """ |
+ collections = [] |
+ methods = [] |
+ resource_name = path.split('.')[-2] |
+ html = [ |
+ '<html><body>', |
+ css, |
+ '<h1>%s</h1>' % breadcrumbs(path[:-1], root_discovery), |
+ '<h2>Instance Methods</h2>' |
+ ] |
+ |
+ # Which methods are for collections. |
+ for name in dir(resource): |
+ if not name.startswith('_') and callable(getattr(resource, name)): |
+ if hasattr(getattr(resource, name), '__is_resource__'): |
+ collections.append(name) |
+ else: |
+ methods.append(name) |
+ |
+ |
+ # TOC |
+ if collections: |
+ for name in collections: |
+ if not name.startswith('_') and callable(getattr(resource, name)): |
+ href = path + name + '.html' |
+ html.append(string.Template(COLLECTION_LINK).substitute( |
+ href=href, name=name)) |
+ |
+ if methods: |
+ for name in methods: |
+ if not name.startswith('_') and callable(getattr(resource, name)): |
+ doc = getattr(resource, name).__doc__ |
+ params = method_params(doc) |
+ firstline = doc.splitlines()[0] |
+ html.append(string.Template(METHOD_LINK).substitute( |
+ name=name, params=params, firstline=firstline)) |
+ |
+ if methods: |
+ html.append('<h3>Method Details</h3>') |
+ for name in methods: |
+ dname = name.rsplit('_')[0] |
+ html.append(method(name, getattr(resource, name).__doc__)) |
+ |
+ html.append('</body></html>') |
+ |
+ return '\n'.join(html) |
+ |
+ |
+def document_collection_recursive(resource, path, root_discovery, discovery): |
+ |
+ html = document_collection(resource, path, root_discovery, discovery) |
+ |
+ f = open(os.path.join(FLAGS.dest, path + 'html'), 'w') |
+ f.write(html.encode('utf-8')) |
+ f.close() |
+ |
+ for name in dir(resource): |
+ if (not name.startswith('_') |
+ and callable(getattr(resource, name)) |
+ and hasattr(getattr(resource, name), '__is_resource__')): |
+ dname = name.rsplit('_')[0] |
+ collection = getattr(resource, name)() |
+ document_collection_recursive(collection, path + name + '.', root_discovery, |
+ discovery['resources'].get(dname, {})) |
+ |
+def document_api(name, version): |
+ """Document the given API. |
+ |
+ Args: |
+ name: string, Name of the API. |
+ version: string, Version of the API. |
+ """ |
+ service = build(name, version) |
+ response, content = http.request( |
+ uritemplate.expand( |
+ FLAGS.discovery_uri_template, { |
+ 'api': name, |
+ 'apiVersion': version}) |
+ ) |
+ discovery = json.loads(content) |
+ |
+ version = safe_version(version) |
+ |
+ document_collection_recursive( |
+ service, '%s_%s.' % (name, version), discovery, discovery) |
+ |
+ |
+def document_api_from_discovery_document(uri): |
+ """Document the given API. |
+ |
+ Args: |
+ uri: string, URI of discovery document. |
+ """ |
+ http = httplib2.Http() |
+ response, content = http.request(FLAGS.discovery_uri) |
+ discovery = json.loads(content) |
+ |
+ service = build_from_document(discovery) |
+ |
+ name = discovery['version'] |
+ version = safe_version(discovery['version']) |
+ |
+ document_collection_recursive( |
+ service, '%s_%s.' % (name, version), discovery, discovery) |
+ |
+ |
+if __name__ == '__main__': |
+ FLAGS = parser.parse_args(sys.argv[1:]) |
+ if FLAGS.discovery_uri: |
+ document_api_from_discovery_document(FLAGS.discovery_uri) |
+ else: |
+ http = httplib2.Http() |
+ resp, content = http.request( |
+ FLAGS.directory_uri, |
+ headers={'X-User-IP': '0.0.0.0'}) |
+ if resp.status == 200: |
+ directory = json.loads(content)['items'] |
+ for api in directory: |
+ document_api(api['name'], api['version']) |
+ else: |
+ sys.exit("Failed to load the discovery document.") |