Initial commit
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import os
|
||||
|
||||
from botocore.docs.service import ServiceDocumenter
|
||||
|
||||
|
||||
def generate_docs(root_dir, session):
|
||||
"""Generates the reference documentation for botocore
|
||||
|
||||
This will go through every available AWS service and output ReSTructured
|
||||
text files documenting each service.
|
||||
|
||||
:param root_dir: The directory to write the reference files to. Each
|
||||
service's reference documentation is loacated at
|
||||
root_dir/reference/services/service-name.rst
|
||||
"""
|
||||
services_doc_path = os.path.join(root_dir, 'reference', 'services')
|
||||
if not os.path.exists(services_doc_path):
|
||||
os.makedirs(services_doc_path)
|
||||
|
||||
# Generate reference docs and write them out.
|
||||
for service_name in session.get_available_services():
|
||||
docs = ServiceDocumenter(service_name, session).document_service()
|
||||
service_doc_path = os.path.join(
|
||||
services_doc_path, service_name + '.rst')
|
||||
with open(service_doc_path, 'wb') as f:
|
||||
f.write(docs)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
# Copyright 2013 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
__version__ = '0.16.0'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,103 @@
|
||||
# Copyright 2012-2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
|
||||
|
||||
DOC_EVENTS = {
|
||||
'doc-breadcrumbs': '.%s',
|
||||
'doc-title': '.%s',
|
||||
'doc-description': '.%s',
|
||||
'doc-synopsis-start': '.%s',
|
||||
'doc-synopsis-option': '.%s.%s',
|
||||
'doc-synopsis-end': '.%s',
|
||||
'doc-options-start': '.%s',
|
||||
'doc-option': '.%s.%s',
|
||||
'doc-option-example': '.%s.%s',
|
||||
'doc-options-end': '.%s',
|
||||
'doc-examples': '.%s',
|
||||
'doc-output': '.%s',
|
||||
'doc-subitems-start': '.%s',
|
||||
'doc-subitem': '.%s.%s',
|
||||
'doc-subitems-end': '.%s',
|
||||
'doc-relateditems-start': '.%s',
|
||||
'doc-relateditem': '.%s.%s',
|
||||
'doc-relateditems-end': '.%s'
|
||||
}
|
||||
|
||||
|
||||
def generate_events(session, help_command):
|
||||
# Now generate the documentation events
|
||||
session.emit('doc-breadcrumbs.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-title.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-description.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-synopsis-start.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
if help_command.arg_table:
|
||||
for arg_name in help_command.arg_table:
|
||||
# An argument can set an '_UNDOCUMENTED' attribute
|
||||
# to True to indicate a parameter that exists
|
||||
# but shouldn't be documented. This can be used
|
||||
# for backwards compatibility of deprecated arguments.
|
||||
if getattr(help_command.arg_table[arg_name],
|
||||
'_UNDOCUMENTED', False):
|
||||
continue
|
||||
session.emit(
|
||||
'doc-synopsis-option.%s.%s' % (help_command.event_class,
|
||||
arg_name),
|
||||
arg_name=arg_name, help_command=help_command)
|
||||
session.emit('doc-synopsis-end.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-options-start.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
if help_command.arg_table:
|
||||
for arg_name in help_command.arg_table:
|
||||
if getattr(help_command.arg_table[arg_name],
|
||||
'_UNDOCUMENTED', False):
|
||||
continue
|
||||
session.emit('doc-option.%s.%s' % (help_command.event_class,
|
||||
arg_name),
|
||||
arg_name=arg_name, help_command=help_command)
|
||||
session.emit('doc-option-example.%s.%s' %
|
||||
(help_command.event_class, arg_name),
|
||||
arg_name=arg_name, help_command=help_command)
|
||||
session.emit('doc-options-end.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-subitems-start.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
if help_command.command_table:
|
||||
for command_name in sorted(help_command.command_table.keys()):
|
||||
if hasattr(help_command.command_table[command_name],
|
||||
'_UNDOCUMENTED'):
|
||||
continue
|
||||
session.emit('doc-subitem.%s.%s'
|
||||
% (help_command.event_class, command_name),
|
||||
command_name=command_name,
|
||||
help_command=help_command)
|
||||
session.emit('doc-subitems-end.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-examples.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-output.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
session.emit('doc-relateditems-start.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
if help_command.related_items:
|
||||
for related_item in sorted(help_command.related_items):
|
||||
session.emit('doc-relateditem.%s.%s'
|
||||
% (help_command.event_class, related_item),
|
||||
help_command=help_command,
|
||||
related_item=related_item)
|
||||
session.emit('doc-relateditems-end.%s' % help_command.event_class,
|
||||
help_command=help_command)
|
||||
@@ -0,0 +1,200 @@
|
||||
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore.compat import six
|
||||
|
||||
|
||||
class DocStringParser(six.moves.html_parser.HTMLParser):
|
||||
"""
|
||||
A simple HTML parser. Focused on converting the subset of HTML
|
||||
that appears in the documentation strings of the JSON models into
|
||||
simple ReST format.
|
||||
"""
|
||||
|
||||
def __init__(self, doc):
|
||||
self.tree = None
|
||||
self.doc = doc
|
||||
six.moves.html_parser.HTMLParser.__init__(self)
|
||||
|
||||
def reset(self):
|
||||
six.moves.html_parser.HTMLParser.reset(self)
|
||||
self.tree = HTMLTree(self.doc)
|
||||
|
||||
def feed(self, data):
|
||||
# HTMLParser is an old style class, so the super() method will not work.
|
||||
six.moves.html_parser.HTMLParser.feed(self, data)
|
||||
self.tree.write()
|
||||
self.tree = HTMLTree(self.doc)
|
||||
|
||||
def close(self):
|
||||
six.moves.html_parser.HTMLParser.close(self)
|
||||
# Write if there is anything remaining.
|
||||
self.tree.write()
|
||||
self.tree = HTMLTree(self.doc)
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
self.tree.add_tag(tag, attrs=attrs)
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
self.tree.add_tag(tag, is_start=False)
|
||||
|
||||
def handle_data(self, data):
|
||||
self.tree.add_data(data)
|
||||
|
||||
|
||||
class HTMLTree(object):
|
||||
"""
|
||||
A tree which handles HTML nodes. Designed to work with a python HTML parser,
|
||||
meaning that the current_node will be the most recently opened tag. When
|
||||
a tag is closed, the current_node moves up to the parent node.
|
||||
"""
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
self.head = StemNode()
|
||||
self.current_node = self.head
|
||||
self.unhandled_tags = []
|
||||
|
||||
def add_tag(self, tag, attrs=None, is_start=True):
|
||||
if not self._doc_has_handler(tag, is_start):
|
||||
self.unhandled_tags.append(tag)
|
||||
return
|
||||
|
||||
if is_start:
|
||||
if tag == 'li':
|
||||
node = LineItemNode(attrs)
|
||||
else:
|
||||
node = TagNode(tag, attrs)
|
||||
self.current_node.add_child(node)
|
||||
self.current_node = node
|
||||
else:
|
||||
self.current_node = self.current_node.parent
|
||||
|
||||
def _doc_has_handler(self, tag, is_start):
|
||||
if is_start:
|
||||
handler_name = 'start_%s' % tag
|
||||
else:
|
||||
handler_name = 'end_%s' % tag
|
||||
|
||||
return hasattr(self.doc.style, handler_name)
|
||||
|
||||
def add_data(self, data):
|
||||
self.current_node.add_child(DataNode(data))
|
||||
|
||||
def write(self):
|
||||
self.head.write(self.doc)
|
||||
|
||||
|
||||
class Node(object):
|
||||
def __init__(self, parent=None):
|
||||
self.parent = parent
|
||||
|
||||
def write(self, doc):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class StemNode(Node):
|
||||
def __init__(self, parent=None):
|
||||
super(StemNode, self).__init__(parent)
|
||||
self.children = []
|
||||
|
||||
def add_child(self, child):
|
||||
child.parent = self
|
||||
self.children.append(child)
|
||||
|
||||
def write(self, doc):
|
||||
self._write_children(doc)
|
||||
|
||||
def _write_children(self, doc):
|
||||
for child in self.children:
|
||||
child.write(doc)
|
||||
|
||||
|
||||
class TagNode(StemNode):
|
||||
"""
|
||||
A generic Tag node. It will verify that handlers exist before writing.
|
||||
"""
|
||||
def __init__(self, tag, attrs=None, parent=None):
|
||||
super(TagNode, self).__init__(parent)
|
||||
self.attrs = attrs
|
||||
self.tag = tag
|
||||
|
||||
def write(self, doc):
|
||||
self._write_start(doc)
|
||||
self._write_children(doc)
|
||||
self._write_end(doc)
|
||||
|
||||
def _write_start(self, doc):
|
||||
handler_name = 'start_%s' % self.tag
|
||||
if hasattr(doc.style, handler_name):
|
||||
getattr(doc.style, handler_name)(self.attrs)
|
||||
|
||||
def _write_end(self, doc):
|
||||
handler_name = 'end_%s' % self.tag
|
||||
if hasattr(doc.style, handler_name):
|
||||
getattr(doc.style, handler_name)()
|
||||
|
||||
|
||||
class LineItemNode(TagNode):
|
||||
def __init__(self, attrs=None, parent=None):
|
||||
super(LineItemNode, self).__init__('li', attrs, parent)
|
||||
|
||||
def write(self, doc):
|
||||
self._lstrip(self)
|
||||
super(LineItemNode, self).write(doc)
|
||||
|
||||
def _lstrip(self, node):
|
||||
"""
|
||||
Traverses the tree, stripping out whitespace until text data is found
|
||||
:param node: The node to strip
|
||||
:return: True if non-whitespace data was found, False otherwise
|
||||
"""
|
||||
for child in node.children:
|
||||
if isinstance(child, DataNode):
|
||||
child.lstrip()
|
||||
if child.data:
|
||||
return True
|
||||
else:
|
||||
found = self._lstrip(child)
|
||||
if found:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class DataNode(Node):
|
||||
"""
|
||||
A Node that contains only string data.
|
||||
"""
|
||||
def __init__(self, data, parent=None):
|
||||
super(DataNode, self).__init__(parent)
|
||||
if not isinstance(data, six.string_types):
|
||||
raise ValueError("Expecting string type, %s given." % type(data))
|
||||
self.data = data
|
||||
|
||||
def lstrip(self):
|
||||
self.data = self.data.lstrip()
|
||||
|
||||
def write(self, doc):
|
||||
if not self.data:
|
||||
return
|
||||
|
||||
if self.data.isspace():
|
||||
str_data = ' '
|
||||
else:
|
||||
end_space = self.data[-1].isspace()
|
||||
words = self.data.split()
|
||||
words = doc.translate_words(words)
|
||||
str_data = ' '.join(words)
|
||||
if end_space:
|
||||
str_data += ' '
|
||||
|
||||
doc.handle_data(str_data)
|
||||
@@ -0,0 +1,218 @@
|
||||
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import logging
|
||||
|
||||
from botocore.compat import OrderedDict
|
||||
from botocore.docs.bcdoc.docstringparser import DocStringParser
|
||||
from botocore.docs.bcdoc.style import ReSTStyle
|
||||
|
||||
LOG = logging.getLogger('bcdocs')
|
||||
|
||||
|
||||
class ReSTDocument(object):
|
||||
|
||||
def __init__(self, target='man'):
|
||||
self.style = ReSTStyle(self)
|
||||
self.target = target
|
||||
self.parser = DocStringParser(self)
|
||||
self.keep_data = True
|
||||
self.do_translation = False
|
||||
self.translation_map = {}
|
||||
self.hrefs = {}
|
||||
self._writes = []
|
||||
self._last_doc_string = None
|
||||
|
||||
def _write(self, s):
|
||||
if self.keep_data and s is not None:
|
||||
self._writes.append(s)
|
||||
|
||||
def write(self, content):
|
||||
"""
|
||||
Write content into the document.
|
||||
"""
|
||||
self._write(content)
|
||||
|
||||
def writeln(self, content):
|
||||
"""
|
||||
Write content on a newline.
|
||||
"""
|
||||
self._write('%s%s\n' % (self.style.spaces(), content))
|
||||
|
||||
def peek_write(self):
|
||||
"""
|
||||
Returns the last content written to the document without
|
||||
removing it from the stack.
|
||||
"""
|
||||
return self._writes[-1]
|
||||
|
||||
def pop_write(self):
|
||||
"""
|
||||
Removes and returns the last content written to the stack.
|
||||
"""
|
||||
return self._writes.pop()
|
||||
|
||||
def push_write(self, s):
|
||||
"""
|
||||
Places new content on the stack.
|
||||
"""
|
||||
self._writes.append(s)
|
||||
|
||||
def getvalue(self):
|
||||
"""
|
||||
Returns the current content of the document as a string.
|
||||
"""
|
||||
if self.hrefs:
|
||||
self.style.new_paragraph()
|
||||
for refname, link in self.hrefs.items():
|
||||
self.style.link_target_definition(refname, link)
|
||||
return ''.join(self._writes).encode('utf-8')
|
||||
|
||||
def translate_words(self, words):
|
||||
return [self.translation_map.get(w, w) for w in words]
|
||||
|
||||
def handle_data(self, data):
|
||||
if data and self.keep_data:
|
||||
self._write(data)
|
||||
|
||||
def include_doc_string(self, doc_string):
|
||||
if doc_string:
|
||||
try:
|
||||
start = len(self._writes)
|
||||
self.parser.feed(doc_string)
|
||||
self.parser.close()
|
||||
end = len(self._writes)
|
||||
self._last_doc_string = (start, end)
|
||||
except Exception:
|
||||
LOG.debug('Error parsing doc string', exc_info=True)
|
||||
LOG.debug(doc_string)
|
||||
|
||||
def remove_last_doc_string(self):
|
||||
# Removes all writes inserted by last doc string
|
||||
if self._last_doc_string is not None:
|
||||
start, end = self._last_doc_string
|
||||
del self._writes[start:end]
|
||||
|
||||
|
||||
class DocumentStructure(ReSTDocument):
|
||||
def __init__(self, name, section_names=None, target='man', context=None):
|
||||
"""Provides a Hierarichial structure to a ReSTDocument
|
||||
|
||||
You can write to it similiar to as you can to a ReSTDocument but
|
||||
has an innate structure for more orginaztion and abstraction.
|
||||
|
||||
:param name: The name of the document
|
||||
:param section_names: A list of sections to be included
|
||||
in the document.
|
||||
:param target: The target documentation of the Document structure
|
||||
:param context: A dictionary of data to store with the strucuture. These
|
||||
are only stored per section not the entire structure.
|
||||
"""
|
||||
super(DocumentStructure, self).__init__(target=target)
|
||||
self._name = name
|
||||
self._structure = OrderedDict()
|
||||
self._path = [self._name]
|
||||
self._context = {}
|
||||
if context is not None:
|
||||
self._context = context
|
||||
if section_names is not None:
|
||||
self._generate_structure(section_names)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the document structure"""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
A list of where to find a particular document structure in the
|
||||
overlying document structure.
|
||||
"""
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, value):
|
||||
self._path = value
|
||||
|
||||
@property
|
||||
def available_sections(self):
|
||||
return list(self._structure)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
return self._context
|
||||
|
||||
def _generate_structure(self, section_names):
|
||||
for section_name in section_names:
|
||||
self.add_new_section(section_name)
|
||||
|
||||
def add_new_section(self, name, context=None):
|
||||
"""Adds a new section to the current document structure
|
||||
|
||||
This document structure will be considered a section to the
|
||||
current document structure but will in itself be an entirely
|
||||
new document structure that can be written to and have sections
|
||||
as well
|
||||
|
||||
:param name: The name of the section.
|
||||
:param context: A dictionary of data to store with the strucuture. These
|
||||
are only stored per section not the entire structure.
|
||||
:rtype: DocumentStructure
|
||||
:returns: A new document structure to add to but lives as a section
|
||||
to the document structure it was instantiated from.
|
||||
"""
|
||||
# Add a new section
|
||||
section = self.__class__(name=name, target=self.target,
|
||||
context=context)
|
||||
section.path = self.path + [name]
|
||||
# Indent the section apporpriately as well
|
||||
section.style.indentation = self.style.indentation
|
||||
section.translation_map = self.translation_map
|
||||
section.hrefs = self.hrefs
|
||||
self._structure[name] = section
|
||||
return section
|
||||
|
||||
def get_section(self, name):
|
||||
"""Retrieve a section"""
|
||||
return self._structure[name]
|
||||
|
||||
def delete_section(self, name):
|
||||
"""Delete a section"""
|
||||
del self._structure[name]
|
||||
|
||||
def flush_structure(self):
|
||||
"""Flushes a doc structure to a ReSTructed string
|
||||
|
||||
The document is flushed out in a DFS style where sections and their
|
||||
subsections' values are added to the string as they are visited.
|
||||
"""
|
||||
# We are at the root flush the links at the beginning of the
|
||||
# document
|
||||
if len(self.path) == 1:
|
||||
if self.hrefs:
|
||||
self.style.new_paragraph()
|
||||
for refname, link in self.hrefs.items():
|
||||
self.style.link_target_definition(refname, link)
|
||||
value = self.getvalue()
|
||||
for name, section in self._structure.items():
|
||||
value += section.flush_structure()
|
||||
return value
|
||||
|
||||
def getvalue(self):
|
||||
return ''.join(self._writes).encode('utf-8')
|
||||
|
||||
def remove_all_sections(self):
|
||||
self._structure = OrderedDict()
|
||||
|
||||
def clear_text(self):
|
||||
self._writes = []
|
||||
@@ -0,0 +1,418 @@
|
||||
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('bcdocs')
|
||||
|
||||
|
||||
class BaseStyle(object):
|
||||
|
||||
def __init__(self, doc, indent_width=2):
|
||||
self.doc = doc
|
||||
self.indent_width = indent_width
|
||||
self._indent = 0
|
||||
self.keep_data = True
|
||||
|
||||
@property
|
||||
def indentation(self):
|
||||
return self._indent
|
||||
|
||||
@indentation.setter
|
||||
def indentation(self, value):
|
||||
self._indent = value
|
||||
|
||||
def new_paragraph(self):
|
||||
return '\n%s' % self.spaces()
|
||||
|
||||
def indent(self):
|
||||
self._indent += 1
|
||||
|
||||
def dedent(self):
|
||||
if self._indent > 0:
|
||||
self._indent -= 1
|
||||
|
||||
def spaces(self):
|
||||
return ' ' * (self._indent * self.indent_width)
|
||||
|
||||
def bold(self, s):
|
||||
return s
|
||||
|
||||
def ref(self, link, title=None):
|
||||
return link
|
||||
|
||||
def h2(self, s):
|
||||
return s
|
||||
|
||||
def h3(self, s):
|
||||
return s
|
||||
|
||||
def underline(self, s):
|
||||
return s
|
||||
|
||||
def italics(self, s):
|
||||
return s
|
||||
|
||||
|
||||
class ReSTStyle(BaseStyle):
|
||||
|
||||
def __init__(self, doc, indent_width=2):
|
||||
BaseStyle.__init__(self, doc, indent_width)
|
||||
self.do_p = True
|
||||
self.a_href = None
|
||||
self.list_depth = 0
|
||||
|
||||
def new_paragraph(self):
|
||||
self.doc.write('\n\n%s' % self.spaces())
|
||||
|
||||
def new_line(self):
|
||||
self.doc.write('\n%s' % self.spaces())
|
||||
|
||||
def _start_inline(self, markup):
|
||||
self.doc.write(markup)
|
||||
|
||||
def _end_inline(self, markup):
|
||||
# Sometimes the HTML markup has whitespace between the end
|
||||
# of the text inside the inline markup and the closing element
|
||||
# (e.g. <b>foobar </b>). This trailing space will cause
|
||||
# problems in the ReST inline markup so we remove it here
|
||||
# by popping the last item written off the stack, striping
|
||||
# the whitespace and then pushing it back on the stack.
|
||||
last_write = self.doc.pop_write().rstrip(' ')
|
||||
|
||||
# Sometimes, for whatever reason, a tag like <b/> is present. This
|
||||
# is problematic because if we simply translate that directly then
|
||||
# we end up with something like ****, which rst will assume is a
|
||||
# heading instead of an empty bold.
|
||||
if last_write == markup:
|
||||
return
|
||||
|
||||
self.doc.push_write(last_write)
|
||||
self.doc.write(markup + ' ')
|
||||
|
||||
def start_bold(self, attrs=None):
|
||||
self._start_inline('**')
|
||||
|
||||
def end_bold(self):
|
||||
self._end_inline('**')
|
||||
|
||||
def start_b(self, attrs=None):
|
||||
self.doc.do_translation = True
|
||||
self.start_bold(attrs)
|
||||
|
||||
def end_b(self):
|
||||
self.doc.do_translation = False
|
||||
self.end_bold()
|
||||
|
||||
def bold(self, s):
|
||||
if s:
|
||||
self.start_bold()
|
||||
self.doc.write(s)
|
||||
self.end_bold()
|
||||
|
||||
def ref(self, title, link=None):
|
||||
if link is None:
|
||||
link = title
|
||||
self.doc.write(':doc:`%s <%s>`' % (title, link))
|
||||
|
||||
def _heading(self, s, border_char):
|
||||
border = border_char * len(s)
|
||||
self.new_paragraph()
|
||||
self.doc.write('%s\n%s\n%s' % (border, s, border))
|
||||
self.new_paragraph()
|
||||
|
||||
def h1(self, s):
|
||||
self._heading(s, '*')
|
||||
|
||||
def h2(self, s):
|
||||
self._heading(s, '=')
|
||||
|
||||
def h3(self, s):
|
||||
self._heading(s, '-')
|
||||
|
||||
def start_italics(self, attrs=None):
|
||||
self._start_inline('*')
|
||||
|
||||
def end_italics(self):
|
||||
self._end_inline('*')
|
||||
|
||||
def italics(self, s):
|
||||
if s:
|
||||
self.start_italics()
|
||||
self.doc.write(s)
|
||||
self.end_italics()
|
||||
|
||||
def start_p(self, attrs=None):
|
||||
if self.do_p:
|
||||
self.doc.write('\n\n%s' % self.spaces())
|
||||
|
||||
def end_p(self):
|
||||
if self.do_p:
|
||||
self.doc.write('\n\n%s' % self.spaces())
|
||||
|
||||
def start_code(self, attrs=None):
|
||||
self.doc.do_translation = True
|
||||
self._start_inline('``')
|
||||
|
||||
def end_code(self):
|
||||
self.doc.do_translation = False
|
||||
self._end_inline('``')
|
||||
|
||||
def code(self, s):
|
||||
if s:
|
||||
self.start_code()
|
||||
self.doc.write(s)
|
||||
self.end_code()
|
||||
|
||||
def start_note(self, attrs=None):
|
||||
self.new_paragraph()
|
||||
self.doc.write('.. note::')
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_note(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_important(self, attrs=None):
|
||||
self.new_paragraph()
|
||||
self.doc.write('.. warning::')
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_important(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_danger(self, attrs=None):
|
||||
self.new_paragraph()
|
||||
self.doc.write('.. danger::')
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_danger(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_a(self, attrs=None):
|
||||
if attrs:
|
||||
for attr_key, attr_value in attrs:
|
||||
if attr_key == 'href':
|
||||
self.a_href = attr_value
|
||||
self.doc.write('`')
|
||||
else:
|
||||
# There are some model documentation that
|
||||
# looks like this: <a>DescribeInstances</a>.
|
||||
# In this case we just write out an empty
|
||||
# string.
|
||||
self.doc.write(' ')
|
||||
self.doc.do_translation = True
|
||||
|
||||
def link_target_definition(self, refname, link):
|
||||
self.doc.writeln('.. _%s: %s' % (refname, link))
|
||||
|
||||
def sphinx_reference_label(self, label, text=None):
|
||||
if text is None:
|
||||
text = label
|
||||
if self.doc.target == 'html':
|
||||
self.doc.write(':ref:`%s <%s>`' % (text, label))
|
||||
else:
|
||||
self.doc.write(text)
|
||||
|
||||
def end_a(self):
|
||||
self.doc.do_translation = False
|
||||
if self.a_href:
|
||||
last_write = self.doc.pop_write()
|
||||
last_write = last_write.rstrip(' ')
|
||||
if last_write and last_write != '`':
|
||||
if ':' in last_write:
|
||||
last_write = last_write.replace(':', r'\:')
|
||||
self.doc.push_write(last_write)
|
||||
self.doc.push_write(' <%s>`__' % self.a_href)
|
||||
elif last_write == '`':
|
||||
# Look at start_a(). It will do a self.doc.write('`')
|
||||
# which is the start of the link title. If that is the
|
||||
# case then there was no link text. We should just
|
||||
# use an inline link. The syntax of this is
|
||||
# `<http://url>`_
|
||||
self.doc.push_write('`<%s>`__' % self.a_href)
|
||||
else:
|
||||
self.doc.push_write(self.a_href)
|
||||
self.doc.hrefs[self.a_href] = self.a_href
|
||||
self.doc.write('`__')
|
||||
self.a_href = None
|
||||
self.doc.write(' ')
|
||||
|
||||
def start_i(self, attrs=None):
|
||||
self.doc.do_translation = True
|
||||
self.start_italics()
|
||||
|
||||
def end_i(self):
|
||||
self.doc.do_translation = False
|
||||
self.end_italics()
|
||||
|
||||
def start_li(self, attrs=None):
|
||||
self.new_line()
|
||||
self.do_p = False
|
||||
self.doc.write('* ')
|
||||
|
||||
def end_li(self):
|
||||
self.do_p = True
|
||||
self.new_line()
|
||||
|
||||
def li(self, s):
|
||||
if s:
|
||||
self.start_li()
|
||||
self.doc.writeln(s)
|
||||
self.end_li()
|
||||
|
||||
def start_ul(self, attrs=None):
|
||||
if self.list_depth != 0:
|
||||
self.indent()
|
||||
self.list_depth += 1
|
||||
self.new_paragraph()
|
||||
|
||||
def end_ul(self):
|
||||
self.list_depth -= 1
|
||||
if self.list_depth != 0:
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_ol(self, attrs=None):
|
||||
# TODO: Need to control the bullets used for LI items
|
||||
if self.list_depth != 0:
|
||||
self.indent()
|
||||
self.list_depth += 1
|
||||
self.new_paragraph()
|
||||
|
||||
def end_ol(self):
|
||||
self.list_depth -= 1
|
||||
if self.list_depth != 0:
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_examples(self, attrs=None):
|
||||
self.doc.keep_data = False
|
||||
|
||||
def end_examples(self):
|
||||
self.doc.keep_data = True
|
||||
|
||||
def start_fullname(self, attrs=None):
|
||||
self.doc.keep_data = False
|
||||
|
||||
def end_fullname(self):
|
||||
self.doc.keep_data = True
|
||||
|
||||
def start_codeblock(self, attrs=None):
|
||||
self.doc.write('::')
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_codeblock(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def codeblock(self, code):
|
||||
"""
|
||||
Literal code blocks are introduced by ending a paragraph with
|
||||
the special marker ::. The literal block must be indented
|
||||
(and, like all paragraphs, separated from the surrounding
|
||||
ones by blank lines).
|
||||
"""
|
||||
self.start_codeblock()
|
||||
self.doc.writeln(code)
|
||||
self.end_codeblock()
|
||||
|
||||
def toctree(self):
|
||||
if self.doc.target == 'html':
|
||||
self.doc.write('\n.. toctree::\n')
|
||||
self.doc.write(' :maxdepth: 1\n')
|
||||
self.doc.write(' :titlesonly:\n\n')
|
||||
else:
|
||||
self.start_ul()
|
||||
|
||||
def tocitem(self, item, file_name=None):
|
||||
if self.doc.target == 'man':
|
||||
self.li(item)
|
||||
else:
|
||||
if file_name:
|
||||
self.doc.writeln(' %s' % file_name)
|
||||
else:
|
||||
self.doc.writeln(' %s' % item)
|
||||
|
||||
def hidden_toctree(self):
|
||||
if self.doc.target == 'html':
|
||||
self.doc.write('\n.. toctree::\n')
|
||||
self.doc.write(' :maxdepth: 1\n')
|
||||
self.doc.write(' :hidden:\n\n')
|
||||
|
||||
def hidden_tocitem(self, item):
|
||||
if self.doc.target == 'html':
|
||||
self.tocitem(item)
|
||||
|
||||
def table_of_contents(self, title=None, depth=None):
|
||||
self.doc.write('.. contents:: ')
|
||||
if title is not None:
|
||||
self.doc.writeln(title)
|
||||
if depth is not None:
|
||||
self.doc.writeln(' :depth: %s' % depth)
|
||||
|
||||
def start_sphinx_py_class(self, class_name):
|
||||
self.new_paragraph()
|
||||
self.doc.write('.. py:class:: %s' % class_name)
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_sphinx_py_class(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_sphinx_py_method(self, method_name, parameters=None):
|
||||
self.new_paragraph()
|
||||
content = '.. py:method:: %s' % method_name
|
||||
if parameters is not None:
|
||||
content += '(%s)' % parameters
|
||||
self.doc.write(content)
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_sphinx_py_method(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def start_sphinx_py_attr(self, attr_name):
|
||||
self.new_paragraph()
|
||||
self.doc.write('.. py:attribute:: %s' % attr_name)
|
||||
self.indent()
|
||||
self.new_paragraph()
|
||||
|
||||
def end_sphinx_py_attr(self):
|
||||
self.dedent()
|
||||
self.new_paragraph()
|
||||
|
||||
def write_py_doc_string(self, docstring):
|
||||
docstring_lines = docstring.splitlines()
|
||||
for docstring_line in docstring_lines:
|
||||
self.doc.writeln(docstring_line)
|
||||
|
||||
def external_link(self, title, link):
|
||||
if self.doc.target == 'html':
|
||||
self.doc.write('`%s <%s>`_' % (title, link))
|
||||
else:
|
||||
self.doc.write(title)
|
||||
|
||||
def internal_link(self, title, page):
|
||||
if self.doc.target == 'html':
|
||||
self.doc.write(':doc:`%s <%s>`' % (title, page))
|
||||
else:
|
||||
self.doc.write(title)
|
||||
@@ -0,0 +1,799 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
|
||||
Custom docutils writer for plain text.
|
||||
Based heavily on the Sphinx text writer. See copyright below.
|
||||
|
||||
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
from docutils import nodes, writers
|
||||
|
||||
|
||||
class TextWrapper(textwrap.TextWrapper):
|
||||
"""Custom subclass that uses a different word separator regex."""
|
||||
|
||||
wordsep_re = re.compile(
|
||||
r'(\s+|' # any whitespace
|
||||
r'(?<=\s)(?::[a-z-]+:)?`\S+|' # interpreted text start
|
||||
r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words
|
||||
r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
|
||||
|
||||
|
||||
MAXWIDTH = 70
|
||||
STDINDENT = 3
|
||||
|
||||
|
||||
def my_wrap(text, width=MAXWIDTH, **kwargs):
|
||||
w = TextWrapper(width=width, **kwargs)
|
||||
return w.wrap(text)
|
||||
|
||||
|
||||
class TextWriter(writers.Writer):
|
||||
supported = ('text',)
|
||||
settings_spec = ('No options here.', '', ())
|
||||
settings_defaults = {}
|
||||
|
||||
output = None
|
||||
|
||||
def __init__(self):
|
||||
writers.Writer.__init__(self)
|
||||
|
||||
def translate(self):
|
||||
visitor = TextTranslator(self.document)
|
||||
self.document.walkabout(visitor)
|
||||
self.output = visitor.body
|
||||
|
||||
|
||||
class TextTranslator(nodes.NodeVisitor):
|
||||
sectionchars = '*=-~"+`'
|
||||
|
||||
def __init__(self, document):
|
||||
nodes.NodeVisitor.__init__(self, document)
|
||||
|
||||
self.nl = os.linesep
|
||||
self.states = [[]]
|
||||
self.stateindent = [0]
|
||||
self.list_counter = []
|
||||
self.sectionlevel = 0
|
||||
self.table = None
|
||||
|
||||
def add_text(self, text):
|
||||
self.states[-1].append((-1, text))
|
||||
|
||||
def new_state(self, indent=STDINDENT):
|
||||
self.states.append([])
|
||||
self.stateindent.append(indent)
|
||||
|
||||
def end_state(self, wrap=True, end=[''], first=None):
|
||||
content = self.states.pop()
|
||||
maxindent = sum(self.stateindent)
|
||||
indent = self.stateindent.pop()
|
||||
result = []
|
||||
toformat = []
|
||||
|
||||
def do_format():
|
||||
if not toformat:
|
||||
return
|
||||
if wrap:
|
||||
res = my_wrap(''.join(toformat), width=MAXWIDTH-maxindent)
|
||||
else:
|
||||
res = ''.join(toformat).splitlines()
|
||||
if end:
|
||||
res += end
|
||||
result.append((indent, res))
|
||||
for itemindent, item in content:
|
||||
if itemindent == -1:
|
||||
toformat.append(item)
|
||||
else:
|
||||
do_format()
|
||||
result.append((indent + itemindent, item))
|
||||
toformat = []
|
||||
do_format()
|
||||
if first is not None and result:
|
||||
itemindent, item = result[0]
|
||||
if item:
|
||||
result.insert(0, (itemindent - indent, [first + item[0]]))
|
||||
result[1] = (itemindent, item[1:])
|
||||
self.states[-1].extend(result)
|
||||
|
||||
def visit_document(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_document(self, node):
|
||||
self.end_state()
|
||||
self.body = self.nl.join(line and (' '*indent + line)
|
||||
for indent, lines in self.states[0]
|
||||
for line in lines)
|
||||
# XXX header/footer?
|
||||
|
||||
def visit_highlightlang(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_section(self, node):
|
||||
self._title_char = self.sectionchars[self.sectionlevel]
|
||||
self.sectionlevel += 1
|
||||
|
||||
def depart_section(self, node):
|
||||
self.sectionlevel -= 1
|
||||
|
||||
def visit_topic(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_topic(self, node):
|
||||
self.end_state()
|
||||
|
||||
visit_sidebar = visit_topic
|
||||
depart_sidebar = depart_topic
|
||||
|
||||
def visit_rubric(self, node):
|
||||
self.new_state(0)
|
||||
self.add_text('-[ ')
|
||||
|
||||
def depart_rubric(self, node):
|
||||
self.add_text(' ]-')
|
||||
self.end_state()
|
||||
|
||||
def visit_compound(self, node):
|
||||
pass
|
||||
|
||||
def depart_compound(self, node):
|
||||
pass
|
||||
|
||||
def visit_glossary(self, node):
|
||||
pass
|
||||
|
||||
def depart_glossary(self, node):
|
||||
pass
|
||||
|
||||
def visit_title(self, node):
|
||||
if isinstance(node.parent, nodes.Admonition):
|
||||
self.add_text(node.astext()+': ')
|
||||
raise nodes.SkipNode
|
||||
self.new_state(0)
|
||||
|
||||
def depart_title(self, node):
|
||||
if isinstance(node.parent, nodes.section):
|
||||
char = self._title_char
|
||||
else:
|
||||
char = '^'
|
||||
text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)
|
||||
self.stateindent.pop()
|
||||
self.states[-1].append((0, ['', text, '%s' % (char * len(text)), '']))
|
||||
|
||||
def visit_subtitle(self, node):
|
||||
pass
|
||||
|
||||
def depart_subtitle(self, node):
|
||||
pass
|
||||
|
||||
def visit_attribution(self, node):
|
||||
self.add_text('-- ')
|
||||
|
||||
def depart_attribution(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc(self, node):
|
||||
pass
|
||||
|
||||
def depart_desc(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
self.new_state(0)
|
||||
if node.parent['objtype'] in ('class', 'exception'):
|
||||
self.add_text('%s ' % node.parent['objtype'])
|
||||
|
||||
def depart_desc_signature(self, node):
|
||||
# XXX: wrap signatures in a way that makes sense
|
||||
self.end_state(wrap=False, end=None)
|
||||
|
||||
def visit_desc_name(self, node):
|
||||
pass
|
||||
|
||||
def depart_desc_name(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_addname(self, node):
|
||||
pass
|
||||
|
||||
def depart_desc_addname(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_type(self, node):
|
||||
pass
|
||||
|
||||
def depart_desc_type(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_returns(self, node):
|
||||
self.add_text(' -> ')
|
||||
|
||||
def depart_desc_returns(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_parameterlist(self, node):
|
||||
self.add_text('(')
|
||||
self.first_param = 1
|
||||
|
||||
def depart_desc_parameterlist(self, node):
|
||||
self.add_text(')')
|
||||
|
||||
def visit_desc_parameter(self, node):
|
||||
if not self.first_param:
|
||||
self.add_text(', ')
|
||||
else:
|
||||
self.first_param = 0
|
||||
self.add_text(node.astext())
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_desc_optional(self, node):
|
||||
self.add_text('[')
|
||||
|
||||
def depart_desc_optional(self, node):
|
||||
self.add_text(']')
|
||||
|
||||
def visit_desc_annotation(self, node):
|
||||
pass
|
||||
|
||||
def depart_desc_annotation(self, node):
|
||||
pass
|
||||
|
||||
def visit_refcount(self, node):
|
||||
pass
|
||||
|
||||
def depart_refcount(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_content(self, node):
|
||||
self.new_state()
|
||||
self.add_text(self.nl)
|
||||
|
||||
def depart_desc_content(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_figure(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_figure(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_caption(self, node):
|
||||
pass
|
||||
|
||||
def depart_caption(self, node):
|
||||
pass
|
||||
|
||||
def visit_productionlist(self, node):
|
||||
self.new_state()
|
||||
names = []
|
||||
for production in node:
|
||||
names.append(production['tokenname'])
|
||||
maxlen = max(len(name) for name in names)
|
||||
for production in node:
|
||||
if production['tokenname']:
|
||||
self.add_text(production['tokenname'].ljust(maxlen) + ' ::=')
|
||||
lastname = production['tokenname']
|
||||
else:
|
||||
self.add_text('%s ' % (' '*len(lastname)))
|
||||
self.add_text(production.astext() + self.nl)
|
||||
self.end_state(wrap=False)
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_seealso(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_seealso(self, node):
|
||||
self.end_state(first='')
|
||||
|
||||
def visit_footnote(self, node):
|
||||
self._footnote = node.children[0].astext().strip()
|
||||
self.new_state(len(self._footnote) + 3)
|
||||
|
||||
def depart_footnote(self, node):
|
||||
self.end_state(first='[%s] ' % self._footnote)
|
||||
|
||||
def visit_citation(self, node):
|
||||
if len(node) and isinstance(node[0], nodes.label):
|
||||
self._citlabel = node[0].astext()
|
||||
else:
|
||||
self._citlabel = ''
|
||||
self.new_state(len(self._citlabel) + 3)
|
||||
|
||||
def depart_citation(self, node):
|
||||
self.end_state(first='[%s] ' % self._citlabel)
|
||||
|
||||
def visit_label(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
# XXX: option list could use some better styling
|
||||
|
||||
def visit_option_list(self, node):
|
||||
pass
|
||||
|
||||
def depart_option_list(self, node):
|
||||
pass
|
||||
|
||||
def visit_option_list_item(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_option_list_item(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_option_group(self, node):
|
||||
self._firstoption = True
|
||||
|
||||
def depart_option_group(self, node):
|
||||
self.add_text(' ')
|
||||
|
||||
def visit_option(self, node):
|
||||
if self._firstoption:
|
||||
self._firstoption = False
|
||||
else:
|
||||
self.add_text(', ')
|
||||
|
||||
def depart_option(self, node):
|
||||
pass
|
||||
|
||||
def visit_option_string(self, node):
|
||||
pass
|
||||
|
||||
def depart_option_string(self, node):
|
||||
pass
|
||||
|
||||
def visit_option_argument(self, node):
|
||||
self.add_text(node['delimiter'])
|
||||
|
||||
def depart_option_argument(self, node):
|
||||
pass
|
||||
|
||||
def visit_description(self, node):
|
||||
pass
|
||||
|
||||
def depart_description(self, node):
|
||||
pass
|
||||
|
||||
def visit_tabular_col_spec(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_colspec(self, node):
|
||||
self.table[0].append(node['colwidth'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_tgroup(self, node):
|
||||
pass
|
||||
|
||||
def depart_tgroup(self, node):
|
||||
pass
|
||||
|
||||
def visit_thead(self, node):
|
||||
pass
|
||||
|
||||
def depart_thead(self, node):
|
||||
pass
|
||||
|
||||
def visit_tbody(self, node):
|
||||
self.table.append('sep')
|
||||
|
||||
def depart_tbody(self, node):
|
||||
pass
|
||||
|
||||
def visit_row(self, node):
|
||||
self.table.append([])
|
||||
|
||||
def depart_row(self, node):
|
||||
pass
|
||||
|
||||
def visit_entry(self, node):
|
||||
if 'morerows' in node or 'morecols' in node:
|
||||
raise NotImplementedError('Column or row spanning cells are '
|
||||
'not implemented.')
|
||||
self.new_state(0)
|
||||
|
||||
def depart_entry(self, node):
|
||||
text = self.nl.join(self.nl.join(x[1]) for x in self.states.pop())
|
||||
self.stateindent.pop()
|
||||
self.table[-1].append(text)
|
||||
|
||||
def visit_table(self, node):
|
||||
if self.table:
|
||||
raise NotImplementedError('Nested tables are not supported.')
|
||||
self.new_state(0)
|
||||
self.table = [[]]
|
||||
|
||||
def depart_table(self, node):
|
||||
lines = self.table[1:]
|
||||
fmted_rows = []
|
||||
colwidths = self.table[0]
|
||||
realwidths = colwidths[:]
|
||||
separator = 0
|
||||
# don't allow paragraphs in table cells for now
|
||||
for line in lines:
|
||||
if line == 'sep':
|
||||
separator = len(fmted_rows)
|
||||
else:
|
||||
cells = []
|
||||
for i, cell in enumerate(line):
|
||||
par = my_wrap(cell, width=colwidths[i])
|
||||
if par:
|
||||
maxwidth = max(map(len, par))
|
||||
else:
|
||||
maxwidth = 0
|
||||
realwidths[i] = max(realwidths[i], maxwidth)
|
||||
cells.append(par)
|
||||
fmted_rows.append(cells)
|
||||
|
||||
def writesep(char='-'):
|
||||
out = ['+']
|
||||
for width in realwidths:
|
||||
out.append(char * (width+2))
|
||||
out.append('+')
|
||||
self.add_text(''.join(out) + self.nl)
|
||||
|
||||
def writerow(row):
|
||||
lines = zip(*row)
|
||||
for line in lines:
|
||||
out = ['|']
|
||||
for i, cell in enumerate(line):
|
||||
if cell:
|
||||
out.append(' ' + cell.ljust(realwidths[i]+1))
|
||||
else:
|
||||
out.append(' ' * (realwidths[i] + 2))
|
||||
out.append('|')
|
||||
self.add_text(''.join(out) + self.nl)
|
||||
|
||||
for i, row in enumerate(fmted_rows):
|
||||
if separator and i == separator:
|
||||
writesep('=')
|
||||
else:
|
||||
writesep('-')
|
||||
writerow(row)
|
||||
writesep('-')
|
||||
self.table = None
|
||||
self.end_state(wrap=False)
|
||||
|
||||
def visit_acks(self, node):
|
||||
self.new_state(0)
|
||||
self.add_text(
|
||||
', '.join(n.astext() for n in node.children[0].children) + '.')
|
||||
self.end_state()
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_image(self, node):
|
||||
if 'alt' in node.attributes:
|
||||
self.add_text(_('[image: %s]') % node['alt'])
|
||||
self.add_text(_('[image]'))
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_transition(self, node):
|
||||
indent = sum(self.stateindent)
|
||||
self.new_state(0)
|
||||
self.add_text('=' * (MAXWIDTH - indent))
|
||||
self.end_state()
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_bullet_list(self, node):
|
||||
self.list_counter.append(-1)
|
||||
|
||||
def depart_bullet_list(self, node):
|
||||
self.list_counter.pop()
|
||||
|
||||
def visit_enumerated_list(self, node):
|
||||
self.list_counter.append(0)
|
||||
|
||||
def depart_enumerated_list(self, node):
|
||||
self.list_counter.pop()
|
||||
|
||||
def visit_definition_list(self, node):
|
||||
self.list_counter.append(-2)
|
||||
|
||||
def depart_definition_list(self, node):
|
||||
self.list_counter.pop()
|
||||
|
||||
def visit_list_item(self, node):
|
||||
if self.list_counter[-1] == -1:
|
||||
# bullet list
|
||||
self.new_state(2)
|
||||
elif self.list_counter[-1] == -2:
|
||||
# definition list
|
||||
pass
|
||||
else:
|
||||
# enumerated list
|
||||
self.list_counter[-1] += 1
|
||||
self.new_state(len(str(self.list_counter[-1])) + 2)
|
||||
|
||||
def depart_list_item(self, node):
|
||||
if self.list_counter[-1] == -1:
|
||||
self.end_state(first='* ', end=None)
|
||||
elif self.list_counter[-1] == -2:
|
||||
pass
|
||||
else:
|
||||
self.end_state(first='%s. ' % self.list_counter[-1], end=None)
|
||||
|
||||
def visit_definition_list_item(self, node):
|
||||
self._li_has_classifier = len(node) >= 2 and \
|
||||
isinstance(node[1], nodes.classifier)
|
||||
|
||||
def depart_definition_list_item(self, node):
|
||||
pass
|
||||
|
||||
def visit_term(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_term(self, node):
|
||||
if not self._li_has_classifier:
|
||||
self.end_state(end=None)
|
||||
|
||||
def visit_termsep(self, node):
|
||||
self.add_text(', ')
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_classifier(self, node):
|
||||
self.add_text(' : ')
|
||||
|
||||
def depart_classifier(self, node):
|
||||
self.end_state(end=None)
|
||||
|
||||
def visit_definition(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_definition(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_field_list(self, node):
|
||||
pass
|
||||
|
||||
def depart_field_list(self, node):
|
||||
pass
|
||||
|
||||
def visit_field(self, node):
|
||||
pass
|
||||
|
||||
def depart_field(self, node):
|
||||
pass
|
||||
|
||||
def visit_field_name(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_field_name(self, node):
|
||||
self.add_text(':')
|
||||
self.end_state(end=None)
|
||||
|
||||
def visit_field_body(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_field_body(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_centered(self, node):
|
||||
pass
|
||||
|
||||
def depart_centered(self, node):
|
||||
pass
|
||||
|
||||
def visit_hlist(self, node):
|
||||
pass
|
||||
|
||||
def depart_hlist(self, node):
|
||||
pass
|
||||
|
||||
def visit_hlistcol(self, node):
|
||||
pass
|
||||
|
||||
def depart_hlistcol(self, node):
|
||||
pass
|
||||
|
||||
def visit_admonition(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_admonition(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_versionmodified(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_versionmodified(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_literal_block(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_literal_block(self, node):
|
||||
self.end_state(wrap=False)
|
||||
|
||||
def visit_doctest_block(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_doctest_block(self, node):
|
||||
self.end_state(wrap=False)
|
||||
|
||||
def visit_line_block(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_line_block(self, node):
|
||||
self.end_state(wrap=False)
|
||||
|
||||
def visit_line(self, node):
|
||||
pass
|
||||
|
||||
def depart_line(self, node):
|
||||
pass
|
||||
|
||||
def visit_block_quote(self, node):
|
||||
self.new_state()
|
||||
|
||||
def depart_block_quote(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_compact_paragraph(self, node):
|
||||
pass
|
||||
|
||||
def depart_compact_paragraph(self, node):
|
||||
pass
|
||||
|
||||
def visit_paragraph(self, node):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_paragraph(self, node):
|
||||
self.end_state()
|
||||
|
||||
def visit_target(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_index(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_substitution_definition(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_pending_xref(self, node):
|
||||
pass
|
||||
|
||||
def depart_pending_xref(self, node):
|
||||
pass
|
||||
|
||||
def visit_reference(self, node):
|
||||
pass
|
||||
|
||||
def depart_reference(self, node):
|
||||
pass
|
||||
|
||||
def visit_download_reference(self, node):
|
||||
pass
|
||||
|
||||
def depart_download_reference(self, node):
|
||||
pass
|
||||
|
||||
def visit_emphasis(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def depart_emphasis(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def visit_literal_emphasis(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def depart_literal_emphasis(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def visit_strong(self, node):
|
||||
self.add_text('**')
|
||||
|
||||
def depart_strong(self, node):
|
||||
self.add_text('**')
|
||||
|
||||
def visit_abbreviation(self, node):
|
||||
self.add_text('')
|
||||
|
||||
def depart_abbreviation(self, node):
|
||||
if node.hasattr('explanation'):
|
||||
self.add_text(' (%s)' % node['explanation'])
|
||||
|
||||
def visit_title_reference(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def depart_title_reference(self, node):
|
||||
self.add_text('*')
|
||||
|
||||
def visit_literal(self, node):
|
||||
self.add_text('"')
|
||||
|
||||
def depart_literal(self, node):
|
||||
self.add_text('"')
|
||||
|
||||
def visit_subscript(self, node):
|
||||
self.add_text('_')
|
||||
|
||||
def depart_subscript(self, node):
|
||||
pass
|
||||
|
||||
def visit_superscript(self, node):
|
||||
self.add_text('^')
|
||||
|
||||
def depart_superscript(self, node):
|
||||
pass
|
||||
|
||||
def visit_footnote_reference(self, node):
|
||||
self.add_text('[%s]' % node.astext())
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_citation_reference(self, node):
|
||||
self.add_text('[%s]' % node.astext())
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_Text(self, node):
|
||||
self.add_text(node.astext())
|
||||
|
||||
def depart_Text(self, node):
|
||||
pass
|
||||
|
||||
def visit_generated(self, node):
|
||||
pass
|
||||
|
||||
def depart_generated(self, node):
|
||||
pass
|
||||
|
||||
def visit_inline(self, node):
|
||||
pass
|
||||
|
||||
def depart_inline(self, node):
|
||||
pass
|
||||
|
||||
def visit_problematic(self, node):
|
||||
self.add_text('>>')
|
||||
|
||||
def depart_problematic(self, node):
|
||||
self.add_text('<<')
|
||||
|
||||
def visit_system_message(self, node):
|
||||
self.new_state(0)
|
||||
self.add_text('<SYSTEM MESSAGE: %s>' % node.astext())
|
||||
self.end_state()
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_comment(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_meta(self, node):
|
||||
# only valid for HTML
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_raw(self, node):
|
||||
if 'text' in node.get('format', '').split():
|
||||
self.body.append(node.astext())
|
||||
raise nodes.SkipNode
|
||||
|
||||
def _visit_admonition(self, node):
|
||||
self.new_state(2)
|
||||
|
||||
def _make_depart_admonition(name):
|
||||
def depart_admonition(self, node):
|
||||
self.end_state(first=name.capitalize() + ': ')
|
||||
return depart_admonition
|
||||
|
||||
visit_attention = _visit_admonition
|
||||
depart_attention = _make_depart_admonition('attention')
|
||||
visit_caution = _visit_admonition
|
||||
depart_caution = _make_depart_admonition('caution')
|
||||
visit_danger = _visit_admonition
|
||||
depart_danger = _make_depart_admonition('danger')
|
||||
visit_error = _visit_admonition
|
||||
depart_error = _make_depart_admonition('error')
|
||||
visit_hint = _visit_admonition
|
||||
depart_hint = _make_depart_admonition('hint')
|
||||
visit_important = _visit_admonition
|
||||
depart_important = _make_depart_admonition('important')
|
||||
visit_note = _visit_admonition
|
||||
depart_note = _make_depart_admonition('note')
|
||||
visit_tip = _visit_admonition
|
||||
depart_tip = _make_depart_admonition('tip')
|
||||
visit_warning = _visit_admonition
|
||||
depart_warning = _make_depart_admonition('warning')
|
||||
|
||||
def unknown_visit(self, node):
|
||||
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
|
||||
@@ -0,0 +1,113 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import inspect
|
||||
|
||||
from botocore.docs.utils import get_official_service_name
|
||||
from botocore.docs.method import document_custom_method
|
||||
from botocore.docs.method import document_model_driven_method
|
||||
from botocore.docs.method import get_instance_public_methods
|
||||
from botocore.docs.sharedexample import document_shared_examples
|
||||
|
||||
|
||||
class ClientDocumenter(object):
|
||||
def __init__(self, client, shared_examples=None):
|
||||
self._client = client
|
||||
self._shared_examples = shared_examples
|
||||
if self._shared_examples is None:
|
||||
self._shared_examples = {}
|
||||
self._service_name = self._client.meta.service_model.service_name
|
||||
|
||||
def document_client(self, section):
|
||||
"""Documents a client and its methods
|
||||
|
||||
:param section: The section to write to.
|
||||
"""
|
||||
self._add_title(section)
|
||||
self._add_class_signature(section)
|
||||
client_methods = get_instance_public_methods(self._client)
|
||||
self._add_client_intro(section, client_methods)
|
||||
self._add_client_methods(section, client_methods)
|
||||
|
||||
def _add_title(self, section):
|
||||
section.style.h2('Client')
|
||||
|
||||
def _add_client_intro(self, section, client_methods):
|
||||
section = section.add_new_section('intro')
|
||||
# Write out the top level description for the client.
|
||||
official_service_name = get_official_service_name(
|
||||
self._client.meta.service_model)
|
||||
section.write(
|
||||
'A low-level client representing %s' % official_service_name)
|
||||
|
||||
# Write out the client example instantiation.
|
||||
self._add_client_creation_example(section)
|
||||
|
||||
# List out all of the possible client methods.
|
||||
section.style.new_line()
|
||||
section.write('These are the available methods:')
|
||||
section.style.new_line()
|
||||
class_name = self._client.__class__.__name__
|
||||
for method_name in sorted(client_methods):
|
||||
section.style.li(':py:meth:`~%s.Client.%s`' % (
|
||||
class_name, method_name))
|
||||
|
||||
def _add_class_signature(self, section):
|
||||
section.style.start_sphinx_py_class(
|
||||
class_name='%s.Client' % self._client.__class__.__name__)
|
||||
|
||||
def _add_client_creation_example(self, section):
|
||||
section.style.start_codeblock()
|
||||
section.style.new_line()
|
||||
section.write(
|
||||
'client = session.create_client(\'{service}\')'.format(
|
||||
service=self._service_name)
|
||||
)
|
||||
section.style.end_codeblock()
|
||||
|
||||
def _add_client_methods(self, section, client_methods):
|
||||
section = section.add_new_section('methods')
|
||||
for method_name in sorted(client_methods):
|
||||
self._add_client_method(
|
||||
section, method_name, client_methods[method_name])
|
||||
|
||||
def _add_client_method(self, section, method_name, method):
|
||||
section = section.add_new_section(method_name)
|
||||
if self._is_custom_method(method_name):
|
||||
self._add_custom_method(section, method_name, method)
|
||||
else:
|
||||
self._add_model_driven_method(section, method_name)
|
||||
|
||||
def _is_custom_method(self, method_name):
|
||||
return method_name not in self._client.meta.method_to_api_mapping
|
||||
|
||||
def _add_custom_method(self, section, method_name, method):
|
||||
document_custom_method(section, method_name, method)
|
||||
|
||||
def _add_model_driven_method(self, section, method_name):
|
||||
service_model = self._client.meta.service_model
|
||||
operation_name = self._client.meta.method_to_api_mapping[method_name]
|
||||
operation_model = service_model.operation_model(operation_name)
|
||||
|
||||
example_prefix = 'response = client.%s' % method_name
|
||||
document_model_driven_method(
|
||||
section, method_name, operation_model,
|
||||
event_emitter=self._client.meta.events,
|
||||
method_description=operation_model.documentation,
|
||||
example_prefix=example_prefix,
|
||||
)
|
||||
|
||||
# Add the shared examples
|
||||
shared_examples = self._shared_examples.get(operation_name)
|
||||
if shared_examples:
|
||||
document_shared_examples(
|
||||
section, operation_model, example_prefix, shared_examples)
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore.docs.method import document_model_driven_method
|
||||
from botocore.docs.waiter import document_wait_method
|
||||
from botocore.docs.paginator import document_paginate_method
|
||||
from botocore.docs.bcdoc.restdoc import DocumentStructure
|
||||
|
||||
|
||||
class LazyLoadedDocstring(str):
|
||||
"""Used for lazily loading docstrings
|
||||
|
||||
You can instantiate this class and assign it to a __doc__ value.
|
||||
The docstring will not be generated till accessed via __doc__ or
|
||||
help(). Note that all docstring classes **must** subclass from
|
||||
this class. It cannot be used directly as a docstring.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
The args and kwargs are the same as the underlying document
|
||||
generation function. These just get proxied to the underlying
|
||||
function.
|
||||
"""
|
||||
super(LazyLoadedDocstring, self).__init__()
|
||||
self._gen_args = args
|
||||
self._gen_kwargs = kwargs
|
||||
self._docstring = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# Needed in order to sub class from str with args and kwargs
|
||||
return super(LazyLoadedDocstring, cls).__new__(cls)
|
||||
|
||||
def _write_docstring(self, *args, **kwargs):
|
||||
raise NotImplementedError(
|
||||
'_write_docstring is not implemented. Please subclass from '
|
||||
'this class and provide your own _write_docstring method'
|
||||
)
|
||||
|
||||
def expandtabs(self, tabsize=8):
|
||||
"""Expands tabs to spaces
|
||||
|
||||
So this is a big hack in order to get lazy loaded docstring work
|
||||
for the ``help()``. In the ``help()`` function, ``pydoc`` and
|
||||
``inspect`` are used. At some point the ``inspect.cleandoc``
|
||||
method is called. To clean the docs ``expandtabs`` is called
|
||||
and that is where we override the method to generate and return the
|
||||
docstrings.
|
||||
"""
|
||||
if self._docstring is None:
|
||||
self._generate()
|
||||
return self._docstring.expandtabs(tabsize)
|
||||
|
||||
def __str__(self):
|
||||
return self._generate()
|
||||
|
||||
# __doc__ of target will use either __repr__ or __str__ of this class.
|
||||
__repr__ = __str__
|
||||
|
||||
def _generate(self):
|
||||
# Generate the docstring if it is not already cached.
|
||||
if self._docstring is None:
|
||||
self._docstring = self._create_docstring()
|
||||
return self._docstring
|
||||
|
||||
def _create_docstring(self):
|
||||
docstring_structure = DocumentStructure('docstring', target='html')
|
||||
# Call the document method function with the args and kwargs
|
||||
# passed to the class.
|
||||
self._write_docstring(
|
||||
docstring_structure, *self._gen_args,
|
||||
**self._gen_kwargs)
|
||||
return docstring_structure.flush_structure().decode('utf-8')
|
||||
|
||||
|
||||
class ClientMethodDocstring(LazyLoadedDocstring):
|
||||
def _write_docstring(self, *args, **kwargs):
|
||||
document_model_driven_method(*args, **kwargs)
|
||||
|
||||
|
||||
class WaiterDocstring(LazyLoadedDocstring):
|
||||
def _write_docstring(self, *args, **kwargs):
|
||||
document_wait_method(*args, **kwargs)
|
||||
|
||||
|
||||
class PaginatorDocstring(LazyLoadedDocstring):
|
||||
def _write_docstring(self, *args, **kwargs):
|
||||
document_paginate_method(*args, **kwargs)
|
||||
@@ -0,0 +1,208 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore.docs.shape import ShapeDocumenter
|
||||
from botocore.docs.utils import py_default
|
||||
|
||||
|
||||
class BaseExampleDocumenter(ShapeDocumenter):
|
||||
def document_example(self, section, shape, prefix=None, include=None,
|
||||
exclude=None):
|
||||
"""Generates an example based on a shape
|
||||
|
||||
:param section: The section to write the documentation to.
|
||||
|
||||
:param shape: The shape of the operation.
|
||||
|
||||
:param prefix: Anything to be included before the example
|
||||
|
||||
:type include: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include: The parameter shapes to include in the documentation.
|
||||
|
||||
:type exclude: List of the names of the parameters to exclude.
|
||||
:param exclude: The names of the parameters to exclude from
|
||||
documentation.
|
||||
"""
|
||||
history = []
|
||||
section.style.new_line()
|
||||
section.style.start_codeblock()
|
||||
if prefix is not None:
|
||||
section.write(prefix)
|
||||
self.traverse_and_document_shape(
|
||||
section=section, shape=shape, history=history,
|
||||
include=include, exclude=exclude)
|
||||
|
||||
def document_recursive_shape(self, section, shape, **kwargs):
|
||||
section.write('{\'... recursive ...\'}')
|
||||
|
||||
def document_shape_default(self, section, shape, history, include=None,
|
||||
exclude=None, **kwargs):
|
||||
py_type = self._get_special_py_default(shape)
|
||||
if py_type is None:
|
||||
py_type = py_default(shape.type_name)
|
||||
|
||||
if self._context.get('streaming_shape') == shape:
|
||||
py_type = 'StreamingBody()'
|
||||
section.write(py_type)
|
||||
|
||||
def document_shape_type_string(self, section, shape, history,
|
||||
include=None, exclude=None, **kwargs):
|
||||
if 'enum' in shape.metadata:
|
||||
for i, enum in enumerate(shape.metadata['enum']):
|
||||
section.write('\'%s\'' % enum)
|
||||
if i < len(shape.metadata['enum']) - 1:
|
||||
section.write('|')
|
||||
else:
|
||||
self.document_shape_default(section, shape, history)
|
||||
|
||||
def document_shape_type_list(self, section, shape, history, include=None,
|
||||
exclude=None, **kwargs):
|
||||
param_shape = shape.member
|
||||
list_section = section.add_new_section('list-value')
|
||||
self._start_nested_param(list_section, '[')
|
||||
param_section = list_section.add_new_section(
|
||||
'member', context={'shape': param_shape.name})
|
||||
self.traverse_and_document_shape(
|
||||
section=param_section, shape=param_shape, history=history)
|
||||
ending_comma_section = list_section.add_new_section('ending-comma')
|
||||
ending_comma_section.write(',')
|
||||
ending_bracket_section = list_section.add_new_section(
|
||||
'ending-bracket')
|
||||
self._end_nested_param(ending_bracket_section, ']')
|
||||
|
||||
def document_shape_type_structure(self, section, shape, history,
|
||||
include=None, exclude=None, **kwargs):
|
||||
if not shape.members:
|
||||
section.write('{}')
|
||||
return
|
||||
|
||||
section = section.add_new_section('structure-value')
|
||||
self._start_nested_param(section, '{')
|
||||
|
||||
input_members = self._add_members_to_shape(shape.members, include)
|
||||
|
||||
for i, param in enumerate(input_members):
|
||||
if exclude and param in exclude:
|
||||
continue
|
||||
param_section = section.add_new_section(param)
|
||||
param_section.write('\'%s\': ' % param)
|
||||
param_shape = input_members[param]
|
||||
param_value_section = param_section.add_new_section(
|
||||
'member-value', context={'shape': param_shape.name})
|
||||
self.traverse_and_document_shape(
|
||||
section=param_value_section, shape=param_shape,
|
||||
history=history, name=param)
|
||||
if i < len(input_members) - 1:
|
||||
ending_comma_section = param_section.add_new_section(
|
||||
'ending-comma')
|
||||
ending_comma_section.write(',')
|
||||
ending_comma_section.style.new_line()
|
||||
self._end_structure(section, '{', '}')
|
||||
|
||||
def document_shape_type_map(self, section, shape, history,
|
||||
include=None, exclude=None, **kwargs):
|
||||
map_section = section.add_new_section('map-value')
|
||||
self._start_nested_param(map_section, '{')
|
||||
value_shape = shape.value
|
||||
key_section = map_section.add_new_section(
|
||||
'key', context={'shape': shape.key.name})
|
||||
key_section.write('\'string\': ')
|
||||
value_section = map_section.add_new_section(
|
||||
'value', context={'shape': value_shape.name})
|
||||
self.traverse_and_document_shape(
|
||||
section=value_section, shape=value_shape, history=history)
|
||||
end_bracket_section = map_section.add_new_section('ending-bracket')
|
||||
self._end_nested_param(end_bracket_section, '}')
|
||||
|
||||
def _add_members_to_shape(self, members, include):
|
||||
if include:
|
||||
members = members.copy()
|
||||
for param in include:
|
||||
members[param.name] = param
|
||||
return members
|
||||
|
||||
def _start_nested_param(self, section, start=None):
|
||||
if start is not None:
|
||||
section.write(start)
|
||||
section.style.indent()
|
||||
section.style.indent()
|
||||
section.style.new_line()
|
||||
|
||||
def _end_nested_param(self, section, end=None):
|
||||
section.style.dedent()
|
||||
section.style.dedent()
|
||||
section.style.new_line()
|
||||
if end is not None:
|
||||
section.write(end)
|
||||
|
||||
def _end_structure(self, section, start, end):
|
||||
# If there are no members in the strucuture, then make sure the
|
||||
# start and the end bracket are on the same line, by removing all
|
||||
# previous text and writing the start and end.
|
||||
if not section.available_sections:
|
||||
section.clear_text()
|
||||
section.write(start + end)
|
||||
self._end_nested_param(section)
|
||||
else:
|
||||
end_bracket_section = section.add_new_section('ending-bracket')
|
||||
self._end_nested_param(end_bracket_section, end)
|
||||
|
||||
|
||||
class ResponseExampleDocumenter(BaseExampleDocumenter):
|
||||
EVENT_NAME = 'response-example'
|
||||
|
||||
def document_shape_type_event_stream(self, section, shape, history,
|
||||
**kwargs):
|
||||
section.write('EventStream(')
|
||||
self.document_shape_type_structure(section, shape, history, **kwargs)
|
||||
end_section = section.add_new_section('event-stream-end')
|
||||
end_section.write(')')
|
||||
|
||||
|
||||
class RequestExampleDocumenter(BaseExampleDocumenter):
|
||||
EVENT_NAME = 'request-example'
|
||||
|
||||
def document_shape_type_structure(self, section, shape, history,
|
||||
include=None, exclude=None, **kwargs):
|
||||
param_format = '\'%s\''
|
||||
operator = ': '
|
||||
start = '{'
|
||||
end = '}'
|
||||
|
||||
if len(history) <= 1:
|
||||
operator = '='
|
||||
start = '('
|
||||
end = ')'
|
||||
param_format = '%s'
|
||||
section = section.add_new_section('structure-value')
|
||||
self._start_nested_param(section, start)
|
||||
input_members = self._add_members_to_shape(shape.members, include)
|
||||
|
||||
for i, param in enumerate(input_members):
|
||||
if exclude and param in exclude:
|
||||
continue
|
||||
param_section = section.add_new_section(param)
|
||||
param_section.write(param_format % param)
|
||||
param_section.write(operator)
|
||||
param_shape = input_members[param]
|
||||
param_value_section = param_section.add_new_section(
|
||||
'member-value', context={'shape': param_shape.name})
|
||||
self.traverse_and_document_shape(
|
||||
section=param_value_section, shape=param_shape,
|
||||
history=history, name=param)
|
||||
if i < len(input_members) - 1:
|
||||
ending_comma_section = param_section.add_new_section(
|
||||
'ending-comma')
|
||||
ending_comma_section.write(',')
|
||||
ending_comma_section.style.new_line()
|
||||
self._end_structure(section, start, end)
|
||||
@@ -0,0 +1,282 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import inspect
|
||||
|
||||
from botocore.docs.params import RequestParamsDocumenter
|
||||
from botocore.docs.params import ResponseParamsDocumenter
|
||||
from botocore.docs.example import ResponseExampleDocumenter
|
||||
from botocore.docs.example import RequestExampleDocumenter
|
||||
|
||||
|
||||
AWS_DOC_BASE = 'https://docs.aws.amazon.com/goto/WebAPI'
|
||||
|
||||
|
||||
def get_instance_public_methods(instance):
|
||||
"""Retrieves an objects public methods
|
||||
|
||||
:param instance: The instance of the class to inspect
|
||||
:rtype: dict
|
||||
:returns: A dictionary that represents an instance's methods where
|
||||
the keys are the name of the methods and the
|
||||
values are the handler to the method.
|
||||
"""
|
||||
instance_members = inspect.getmembers(instance)
|
||||
instance_methods = {}
|
||||
for name, member in instance_members:
|
||||
if not name.startswith('_'):
|
||||
if inspect.ismethod(member):
|
||||
instance_methods[name] = member
|
||||
return instance_methods
|
||||
|
||||
|
||||
def document_model_driven_signature(section, name, operation_model,
|
||||
include=None, exclude=None):
|
||||
"""Documents the signature of a model-driven method
|
||||
|
||||
:param section: The section to write the documentation to.
|
||||
|
||||
:param name: The name of the method
|
||||
|
||||
:param operation_model: The operation model for the method
|
||||
|
||||
:type include: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include: The parameter shapes to include in the documentation.
|
||||
|
||||
:type exclude: List of the names of the parameters to exclude.
|
||||
:param exclude: The names of the parameters to exclude from
|
||||
documentation.
|
||||
"""
|
||||
params = {}
|
||||
if operation_model.input_shape:
|
||||
params = operation_model.input_shape.members
|
||||
|
||||
parameter_names = list(params.keys())
|
||||
|
||||
if include is not None:
|
||||
for member in include:
|
||||
parameter_names.append(member.name)
|
||||
|
||||
if exclude is not None:
|
||||
for member in exclude:
|
||||
if member in parameter_names:
|
||||
parameter_names.remove(member)
|
||||
|
||||
signature_params = ''
|
||||
if parameter_names:
|
||||
signature_params = '**kwargs'
|
||||
section.style.start_sphinx_py_method(name, signature_params)
|
||||
|
||||
|
||||
def document_custom_signature(section, name, method,
|
||||
include=None, exclude=None):
|
||||
"""Documents the signature of a custom method
|
||||
|
||||
:param section: The section to write the documentation to.
|
||||
|
||||
:param name: The name of the method
|
||||
|
||||
:param method: The handle to the method being documented
|
||||
|
||||
:type include: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include: The parameter shapes to include in the documentation.
|
||||
|
||||
:type exclude: List of the names of the parameters to exclude.
|
||||
:param exclude: The names of the parameters to exclude from
|
||||
documentation.
|
||||
"""
|
||||
args, varargs, keywords, defaults = inspect.getargspec(method)
|
||||
args = args[1:]
|
||||
signature_params = inspect.formatargspec(
|
||||
args, varargs, keywords, defaults)
|
||||
signature_params = signature_params.lstrip('(')
|
||||
signature_params = signature_params.rstrip(')')
|
||||
section.style.start_sphinx_py_method(name, signature_params)
|
||||
|
||||
|
||||
def document_custom_method(section, method_name, method):
|
||||
"""Documents a non-data driven method
|
||||
|
||||
:param section: The section to write the documentation to.
|
||||
|
||||
:param method_name: The name of the method
|
||||
|
||||
:param method: The handle to the method being documented
|
||||
"""
|
||||
document_custom_signature(
|
||||
section, method_name, method)
|
||||
method_intro_section = section.add_new_section('method-intro')
|
||||
method_intro_section.writeln('')
|
||||
doc_string = inspect.getdoc(method)
|
||||
if doc_string is not None:
|
||||
method_intro_section.style.write_py_doc_string(doc_string)
|
||||
|
||||
|
||||
def document_model_driven_method(section, method_name, operation_model,
|
||||
event_emitter, method_description=None,
|
||||
example_prefix=None, include_input=None,
|
||||
include_output=None, exclude_input=None,
|
||||
exclude_output=None, document_output=True,
|
||||
include_signature=True):
|
||||
"""Documents an individual method
|
||||
|
||||
:param section: The section to write to
|
||||
|
||||
:param method_name: The name of the method
|
||||
|
||||
:param operation_model: The model of the operation
|
||||
|
||||
:param event_emitter: The event emitter to use to emit events
|
||||
|
||||
:param example_prefix: The prefix to use in the method example.
|
||||
|
||||
:type include_input: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include_input: The parameter shapes to include in the
|
||||
input documentation.
|
||||
|
||||
:type include_output: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include_input: The parameter shapes to include in the
|
||||
output documentation.
|
||||
|
||||
:type exclude_input: List of the names of the parameters to exclude.
|
||||
:param exclude_input: The names of the parameters to exclude from
|
||||
input documentation.
|
||||
|
||||
:type exclude_output: List of the names of the parameters to exclude.
|
||||
:param exclude_input: The names of the parameters to exclude from
|
||||
output documentation.
|
||||
|
||||
:param document_output: A boolean flag to indicate whether to
|
||||
document the output.
|
||||
|
||||
:param include_signature: Whether or not to include the signature.
|
||||
It is useful for generating docstrings.
|
||||
"""
|
||||
# Add the signature if specified.
|
||||
if include_signature:
|
||||
document_model_driven_signature(
|
||||
section, method_name, operation_model, include=include_input,
|
||||
exclude=exclude_input)
|
||||
|
||||
# Add the description for the method.
|
||||
method_intro_section = section.add_new_section('method-intro')
|
||||
method_intro_section.include_doc_string(method_description)
|
||||
if operation_model.deprecated:
|
||||
method_intro_section.style.start_danger()
|
||||
method_intro_section.writeln(
|
||||
'This operation is deprecated and may not function as '
|
||||
'expected. This operation should not be used going forward '
|
||||
'and is only kept for the purpose of backwards compatiblity.')
|
||||
method_intro_section.style.end_danger()
|
||||
service_uid = operation_model.service_model.metadata.get('uid')
|
||||
if service_uid is not None:
|
||||
method_intro_section.style.new_paragraph()
|
||||
method_intro_section.write("See also: ")
|
||||
link = '%s/%s/%s' % (AWS_DOC_BASE, service_uid,
|
||||
operation_model.name)
|
||||
method_intro_section.style.external_link(title="AWS API Documentation",
|
||||
link=link)
|
||||
method_intro_section.writeln('')
|
||||
|
||||
# Add the example section.
|
||||
example_section = section.add_new_section('example')
|
||||
example_section.style.new_paragraph()
|
||||
example_section.style.bold('Request Syntax')
|
||||
|
||||
context = {
|
||||
'special_shape_types': {
|
||||
'streaming_input_shape': operation_model.get_streaming_input(),
|
||||
'streaming_output_shape': operation_model.get_streaming_output(),
|
||||
'eventstream_output_shape': operation_model.get_event_stream_output(),
|
||||
},
|
||||
}
|
||||
|
||||
if operation_model.input_shape:
|
||||
RequestExampleDocumenter(
|
||||
service_name=operation_model.service_model.service_name,
|
||||
operation_name=operation_model.name,
|
||||
event_emitter=event_emitter, context=context).document_example(
|
||||
example_section, operation_model.input_shape,
|
||||
prefix=example_prefix, include=include_input,
|
||||
exclude=exclude_input)
|
||||
else:
|
||||
example_section.style.new_paragraph()
|
||||
example_section.style.start_codeblock()
|
||||
example_section.write(example_prefix + '()')
|
||||
|
||||
# Add the request parameter documentation.
|
||||
request_params_section = section.add_new_section('request-params')
|
||||
if operation_model.input_shape:
|
||||
RequestParamsDocumenter(
|
||||
service_name=operation_model.service_model.service_name,
|
||||
operation_name=operation_model.name,
|
||||
event_emitter=event_emitter, context=context).document_params(
|
||||
request_params_section, operation_model.input_shape,
|
||||
include=include_input, exclude=exclude_input)
|
||||
|
||||
# Add the return value documentation
|
||||
return_section = section.add_new_section('return')
|
||||
return_section.style.new_line()
|
||||
if operation_model.output_shape is not None and document_output:
|
||||
return_section.write(':rtype: dict')
|
||||
return_section.style.new_line()
|
||||
return_section.write(':returns: ')
|
||||
return_section.style.indent()
|
||||
return_section.style.new_line()
|
||||
|
||||
# If the operation is an event stream, describe the tagged union
|
||||
event_stream_output = operation_model.get_event_stream_output()
|
||||
if event_stream_output:
|
||||
event_section = return_section.add_new_section('event-stream')
|
||||
event_section.style.new_paragraph()
|
||||
event_section.write(
|
||||
'The response of this operation contains an '
|
||||
':class:`.EventStream` member. When iterated the '
|
||||
':class:`.EventStream` will yield events based on the '
|
||||
'structure below, where only one of the top level keys '
|
||||
'will be present for any given event.'
|
||||
)
|
||||
event_section.style.new_line()
|
||||
|
||||
# Add an example return value
|
||||
return_example_section = return_section.add_new_section('example')
|
||||
return_example_section.style.new_line()
|
||||
return_example_section.style.bold('Response Syntax')
|
||||
return_example_section.style.new_paragraph()
|
||||
ResponseExampleDocumenter(
|
||||
service_name=operation_model.service_model.service_name,
|
||||
operation_name=operation_model.name,
|
||||
event_emitter=event_emitter,
|
||||
context=context).document_example(
|
||||
return_example_section, operation_model.output_shape,
|
||||
include=include_output, exclude=exclude_output)
|
||||
|
||||
# Add a description for the return value
|
||||
return_description_section = return_section.add_new_section(
|
||||
'description')
|
||||
return_description_section.style.new_line()
|
||||
return_description_section.style.bold('Response Structure')
|
||||
return_description_section.style.new_paragraph()
|
||||
ResponseParamsDocumenter(
|
||||
service_name=operation_model.service_model.service_name,
|
||||
operation_name=operation_model.name,
|
||||
event_emitter=event_emitter,
|
||||
context=context).document_params(
|
||||
return_description_section, operation_model.output_shape,
|
||||
include=include_output, exclude=exclude_output)
|
||||
else:
|
||||
return_section.write(':returns: None')
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore import xform_name
|
||||
from botocore.compat import OrderedDict
|
||||
from botocore.docs.utils import DocumentedShape
|
||||
from botocore.utils import get_service_module_name
|
||||
from botocore.docs.method import document_model_driven_method
|
||||
|
||||
|
||||
class PaginatorDocumenter(object):
|
||||
def __init__(self, client, service_paginator_model):
|
||||
self._client = client
|
||||
self._service_name = self._client.meta.service_model.service_name
|
||||
self._service_paginator_model = service_paginator_model
|
||||
|
||||
def document_paginators(self, section):
|
||||
"""Documents the various paginators for a service
|
||||
|
||||
param section: The section to write to.
|
||||
"""
|
||||
section.style.h2('Paginators')
|
||||
section.style.new_line()
|
||||
section.writeln('The available paginators are:')
|
||||
|
||||
paginator_names = sorted(
|
||||
self._service_paginator_model._paginator_config)
|
||||
|
||||
# List the available paginators and then document each paginator.
|
||||
for paginator_name in paginator_names:
|
||||
section.style.li(
|
||||
':py:class:`%s.Paginator.%s`' % (
|
||||
self._client.__class__.__name__, paginator_name))
|
||||
self._add_paginator(section, paginator_name)
|
||||
|
||||
def _add_paginator(self, section, paginator_name):
|
||||
section = section.add_new_section(paginator_name)
|
||||
|
||||
# Docment the paginator class
|
||||
section.style.start_sphinx_py_class(
|
||||
class_name='%s.Paginator.%s' % (
|
||||
self._client.__class__.__name__, paginator_name))
|
||||
section.style.start_codeblock()
|
||||
section.style.new_line()
|
||||
|
||||
# Document how to instantiate the paginator.
|
||||
section.write(
|
||||
'paginator = client.get_paginator(\'%s\')' % xform_name(
|
||||
paginator_name)
|
||||
)
|
||||
section.style.end_codeblock()
|
||||
section.style.new_line()
|
||||
# Get the pagination model for the particular paginator.
|
||||
paginator_config = self._service_paginator_model.get_paginator(
|
||||
paginator_name)
|
||||
document_paginate_method(
|
||||
section=section,
|
||||
paginator_name=paginator_name,
|
||||
event_emitter=self._client.meta.events,
|
||||
service_model=self._client.meta.service_model,
|
||||
paginator_config=paginator_config
|
||||
)
|
||||
|
||||
|
||||
def document_paginate_method(section, paginator_name, event_emitter,
|
||||
service_model, paginator_config,
|
||||
include_signature=True):
|
||||
"""Documents the paginate method of a paginator
|
||||
|
||||
:param section: The section to write to
|
||||
|
||||
:param paginator_name: The name of the paginator. It is snake cased.
|
||||
|
||||
:param event_emitter: The event emitter to use to emit events
|
||||
|
||||
:param service_model: The service model
|
||||
|
||||
:param paginator_config: The paginator config associated to a particular
|
||||
paginator.
|
||||
|
||||
:param include_signature: Whether or not to include the signature.
|
||||
It is useful for generating docstrings.
|
||||
"""
|
||||
# Retrieve the operation model of the underlying operation.
|
||||
operation_model = service_model.operation_model(
|
||||
paginator_name)
|
||||
|
||||
# Add representations of the request and response parameters
|
||||
# we want to include in the description of the paginate method.
|
||||
# These are parameters we expose via the botocore interface.
|
||||
pagination_config_members = OrderedDict()
|
||||
|
||||
pagination_config_members['MaxItems'] = DocumentedShape(
|
||||
name='MaxItems', type_name='integer',
|
||||
documentation=(
|
||||
'<p>The total number of items to return. If the total '
|
||||
'number of items available is more than the value '
|
||||
'specified in max-items then a <code>NextToken</code> '
|
||||
'will be provided in the output that you can use to '
|
||||
'resume pagination.</p>'))
|
||||
|
||||
if paginator_config.get('limit_key', None):
|
||||
pagination_config_members['PageSize'] = DocumentedShape(
|
||||
name='PageSize', type_name='integer',
|
||||
documentation='<p>The size of each page.<p>')
|
||||
|
||||
pagination_config_members['StartingToken'] = DocumentedShape(
|
||||
name='StartingToken', type_name='string',
|
||||
documentation=(
|
||||
'<p>A token to specify where to start paginating. '
|
||||
'This is the <code>NextToken</code> from a previous '
|
||||
'response.</p>'))
|
||||
|
||||
botocore_pagination_params = [
|
||||
DocumentedShape(
|
||||
name='PaginationConfig', type_name='structure',
|
||||
documentation=(
|
||||
'<p>A dictionary that provides parameters to control '
|
||||
'pagination.</p>'),
|
||||
members=pagination_config_members)
|
||||
]
|
||||
|
||||
botocore_pagination_response_params = [
|
||||
DocumentedShape(
|
||||
name='NextToken', type_name='string',
|
||||
documentation=(
|
||||
'<p>A token to resume pagination.</p>'))
|
||||
]
|
||||
|
||||
service_pagination_params = []
|
||||
|
||||
# Add the normal input token of the method to a list
|
||||
# of input paramters that we wish to hide since we expose our own.
|
||||
if isinstance(paginator_config['input_token'], list):
|
||||
service_pagination_params += paginator_config['input_token']
|
||||
else:
|
||||
service_pagination_params.append(paginator_config['input_token'])
|
||||
|
||||
# Hide the limit key in the documentation.
|
||||
if paginator_config.get('limit_key', None):
|
||||
service_pagination_params.append(paginator_config['limit_key'])
|
||||
|
||||
# Hide the output tokens in the documentation.
|
||||
service_pagination_response_params = []
|
||||
if isinstance(paginator_config['output_token'], list):
|
||||
service_pagination_response_params += paginator_config[
|
||||
'output_token']
|
||||
else:
|
||||
service_pagination_response_params.append(paginator_config[
|
||||
'output_token'])
|
||||
|
||||
paginate_description = (
|
||||
'Creates an iterator that will paginate through responses '
|
||||
'from :py:meth:`{0}.Client.{1}`.'.format(
|
||||
get_service_module_name(service_model), xform_name(paginator_name))
|
||||
)
|
||||
|
||||
document_model_driven_method(
|
||||
section, 'paginate', operation_model,
|
||||
event_emitter=event_emitter,
|
||||
method_description=paginate_description,
|
||||
example_prefix='response_iterator = paginator.paginate',
|
||||
include_input=botocore_pagination_params,
|
||||
include_output=botocore_pagination_response_params,
|
||||
exclude_input=service_pagination_params,
|
||||
exclude_output=service_pagination_response_params,
|
||||
include_signature=include_signature
|
||||
)
|
||||
@@ -0,0 +1,220 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore.docs.shape import ShapeDocumenter
|
||||
from botocore.docs.utils import py_type_name
|
||||
|
||||
|
||||
class BaseParamsDocumenter(ShapeDocumenter):
|
||||
def document_params(self, section, shape, include=None, exclude=None):
|
||||
"""Fills out the documentation for a section given a model shape.
|
||||
|
||||
:param section: The section to write the documentation to.
|
||||
|
||||
:param shape: The shape of the operation.
|
||||
|
||||
:type include: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include: The parameter shapes to include in the documentation.
|
||||
|
||||
:type exclude: List of the names of the parameters to exclude.
|
||||
:param exclude: The names of the parameters to exclude from
|
||||
documentation.
|
||||
"""
|
||||
history = []
|
||||
self.traverse_and_document_shape(
|
||||
section=section, shape=shape, history=history,
|
||||
name=None, include=include, exclude=exclude)
|
||||
|
||||
def document_recursive_shape(self, section, shape, **kwargs):
|
||||
self._add_member_documentation(section, shape, **kwargs)
|
||||
|
||||
def document_shape_default(self, section, shape, history, include=None,
|
||||
exclude=None, **kwargs):
|
||||
self._add_member_documentation(section, shape, **kwargs)
|
||||
|
||||
def document_shape_type_list(self, section, shape, history, include=None,
|
||||
exclude=None, **kwargs):
|
||||
self._add_member_documentation(section, shape, **kwargs)
|
||||
param_shape = shape.member
|
||||
param_section = section.add_new_section(
|
||||
param_shape.name, context={'shape': shape.member.name})
|
||||
self._start_nested_param(param_section)
|
||||
self.traverse_and_document_shape(
|
||||
section=param_section, shape=param_shape,
|
||||
history=history, name=None)
|
||||
section = section.add_new_section('end-list')
|
||||
self._end_nested_param(section)
|
||||
|
||||
def document_shape_type_map(self, section, shape, history, include=None,
|
||||
exclude=None, **kwargs):
|
||||
self._add_member_documentation(section, shape, **kwargs)
|
||||
|
||||
key_section = section.add_new_section(
|
||||
'key', context={'shape': shape.key.name})
|
||||
self._start_nested_param(key_section)
|
||||
self._add_member_documentation(key_section, shape.key)
|
||||
|
||||
param_section = section.add_new_section(
|
||||
shape.value.name, context={'shape': shape.value.name})
|
||||
param_section.style.indent()
|
||||
self._start_nested_param(param_section)
|
||||
self.traverse_and_document_shape(
|
||||
section=param_section, shape=shape.value,
|
||||
history=history, name=None)
|
||||
|
||||
end_section = section.add_new_section('end-map')
|
||||
self._end_nested_param(end_section)
|
||||
self._end_nested_param(end_section)
|
||||
|
||||
def document_shape_type_structure(self, section, shape, history,
|
||||
include=None, exclude=None,
|
||||
name=None, **kwargs):
|
||||
members = self._add_members_to_shape(shape.members, include)
|
||||
self._add_member_documentation(section, shape, name=name)
|
||||
for param in members:
|
||||
if exclude and param in exclude:
|
||||
continue
|
||||
param_shape = members[param]
|
||||
param_section = section.add_new_section(
|
||||
param, context={'shape': param_shape.name})
|
||||
self._start_nested_param(param_section)
|
||||
self.traverse_and_document_shape(
|
||||
section=param_section, shape=param_shape,
|
||||
history=history, name=param)
|
||||
section = section.add_new_section('end-structure')
|
||||
self._end_nested_param(section)
|
||||
|
||||
def _add_member_documentation(self, section, shape, **kwargs):
|
||||
pass
|
||||
|
||||
def _add_members_to_shape(self, members, include):
|
||||
if include:
|
||||
members = members.copy()
|
||||
for param in include:
|
||||
members[param.name] = param
|
||||
return members
|
||||
|
||||
def _document_non_top_level_param_type(self, type_section, shape):
|
||||
special_py_type = self._get_special_py_type_name(shape)
|
||||
py_type = py_type_name(shape.type_name)
|
||||
|
||||
type_format = '(%s) -- '
|
||||
if special_py_type is not None:
|
||||
# Special type can reference a linked class.
|
||||
# Italicizing it blows away the link.
|
||||
type_section.write(type_format % special_py_type)
|
||||
else:
|
||||
type_section.style.italics(type_format % py_type)
|
||||
|
||||
def _start_nested_param(self, section):
|
||||
section.style.indent()
|
||||
section.style.new_line()
|
||||
|
||||
def _end_nested_param(self, section):
|
||||
section.style.dedent()
|
||||
section.style.new_line()
|
||||
|
||||
|
||||
class ResponseParamsDocumenter(BaseParamsDocumenter):
|
||||
"""Generates the description for the response parameters"""
|
||||
|
||||
EVENT_NAME = 'response-params'
|
||||
|
||||
def _add_member_documentation(self, section, shape, name=None, **kwargs):
|
||||
name_section = section.add_new_section('param-name')
|
||||
name_section.write('- ')
|
||||
if name is not None:
|
||||
name_section.style.bold('%s ' % name)
|
||||
type_section = section.add_new_section('param-type')
|
||||
self._document_non_top_level_param_type(type_section, shape)
|
||||
|
||||
documentation_section = section.add_new_section('param-documentation')
|
||||
if shape.documentation:
|
||||
documentation_section.style.indent()
|
||||
documentation_section.include_doc_string(shape.documentation)
|
||||
section.style.new_paragraph()
|
||||
|
||||
def document_shape_type_event_stream(self, section, shape, history,
|
||||
**kwargs):
|
||||
self.document_shape_type_structure(section, shape, history, **kwargs)
|
||||
|
||||
|
||||
class RequestParamsDocumenter(BaseParamsDocumenter):
|
||||
"""Generates the description for the request parameters"""
|
||||
|
||||
EVENT_NAME = 'request-params'
|
||||
|
||||
def document_shape_type_structure(self, section, shape, history,
|
||||
include=None, exclude=None, **kwargs):
|
||||
if len(history) > 1:
|
||||
self._add_member_documentation(section, shape, **kwargs)
|
||||
section.style.indent()
|
||||
members = self._add_members_to_shape(shape.members, include)
|
||||
for i, param in enumerate(members):
|
||||
if exclude and param in exclude:
|
||||
continue
|
||||
param_shape = members[param]
|
||||
param_section = section.add_new_section(
|
||||
param, context={'shape': param_shape.name})
|
||||
param_section.style.new_line()
|
||||
is_required = param in shape.required_members
|
||||
self.traverse_and_document_shape(
|
||||
section=param_section, shape=param_shape,
|
||||
history=history, name=param, is_required=is_required)
|
||||
section = section.add_new_section('end-structure')
|
||||
if len(history) > 1:
|
||||
section.style.dedent()
|
||||
section.style.new_line()
|
||||
|
||||
def _add_member_documentation(self, section, shape, name=None,
|
||||
is_top_level_param=False, is_required=False,
|
||||
**kwargs):
|
||||
py_type = self._get_special_py_type_name(shape)
|
||||
if py_type is None:
|
||||
py_type = py_type_name(shape.type_name)
|
||||
if is_top_level_param:
|
||||
type_section = section.add_new_section('param-type')
|
||||
type_section.write(':type %s: %s' % (name, py_type))
|
||||
end_type_section = type_section.add_new_section('end-param-type')
|
||||
end_type_section.style.new_line()
|
||||
name_section = section.add_new_section('param-name')
|
||||
name_section.write(':param %s: ' % name)
|
||||
|
||||
else:
|
||||
name_section = section.add_new_section('param-name')
|
||||
name_section.write('- ')
|
||||
if name is not None:
|
||||
name_section.style.bold('%s ' % name)
|
||||
type_section = section.add_new_section('param-type')
|
||||
self._document_non_top_level_param_type(type_section, shape)
|
||||
|
||||
if is_required:
|
||||
is_required_section = section.add_new_section('is-required')
|
||||
is_required_section.style.indent()
|
||||
is_required_section.style.bold('[REQUIRED] ')
|
||||
if shape.documentation:
|
||||
documentation_section = section.add_new_section(
|
||||
'param-documentation')
|
||||
documentation_section.style.indent()
|
||||
documentation_section.include_doc_string(shape.documentation)
|
||||
self._add_special_trait_documentation(documentation_section, shape)
|
||||
end_param_section = section.add_new_section('end-param')
|
||||
end_param_section.style.new_paragraph()
|
||||
|
||||
def _add_special_trait_documentation(self, section, shape):
|
||||
if 'idempotencyToken' in shape.metadata:
|
||||
self._append_idempotency_documentation(section)
|
||||
|
||||
def _append_idempotency_documentation(self, section):
|
||||
docstring = 'This field is autopopulated if not provided.'
|
||||
section.write(docstring)
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore.exceptions import DataNotFoundError
|
||||
from botocore.docs.utils import get_official_service_name
|
||||
from botocore.docs.client import ClientDocumenter
|
||||
from botocore.docs.waiter import WaiterDocumenter
|
||||
from botocore.docs.paginator import PaginatorDocumenter
|
||||
from botocore.docs.bcdoc.restdoc import DocumentStructure
|
||||
|
||||
|
||||
class ServiceDocumenter(object):
|
||||
def __init__(self, service_name, session):
|
||||
self._session = session
|
||||
self._service_name = service_name
|
||||
|
||||
self._client = self._session.create_client(
|
||||
service_name, region_name='us-east-1', aws_access_key_id='foo',
|
||||
aws_secret_access_key='bar')
|
||||
self._event_emitter = self._client.meta.events
|
||||
|
||||
self.sections = [
|
||||
'title',
|
||||
'table-of-contents',
|
||||
'client-api',
|
||||
'paginator-api',
|
||||
'waiter-api'
|
||||
]
|
||||
|
||||
def document_service(self):
|
||||
"""Documents an entire service.
|
||||
|
||||
:returns: The reStructured text of the documented service.
|
||||
"""
|
||||
doc_structure = DocumentStructure(
|
||||
self._service_name, section_names=self.sections,
|
||||
target='html')
|
||||
self.title(doc_structure.get_section('title'))
|
||||
self.table_of_contents(doc_structure.get_section('table-of-contents'))
|
||||
self.client_api(doc_structure.get_section('client-api'))
|
||||
self.paginator_api(doc_structure.get_section('paginator-api'))
|
||||
self.waiter_api(doc_structure.get_section('waiter-api'))
|
||||
return doc_structure.flush_structure()
|
||||
|
||||
def title(self, section):
|
||||
section.style.h1(self._client.__class__.__name__)
|
||||
self._event_emitter.emit(
|
||||
'docs.%s.%s' % ('title',
|
||||
self._service_name),
|
||||
section=section
|
||||
)
|
||||
|
||||
def table_of_contents(self, section):
|
||||
section.style.table_of_contents(title='Table of Contents', depth=2)
|
||||
|
||||
def client_api(self, section):
|
||||
examples = None
|
||||
try:
|
||||
examples = self.get_examples(self._service_name)
|
||||
except DataNotFoundError:
|
||||
pass
|
||||
|
||||
ClientDocumenter(self._client, examples).document_client(section)
|
||||
|
||||
def paginator_api(self, section):
|
||||
try:
|
||||
service_paginator_model = self._session.get_paginator_model(
|
||||
self._service_name)
|
||||
except DataNotFoundError:
|
||||
return
|
||||
paginator_documenter = PaginatorDocumenter(
|
||||
self._client, service_paginator_model)
|
||||
paginator_documenter.document_paginators(section)
|
||||
|
||||
def waiter_api(self, section):
|
||||
if self._client.waiter_names:
|
||||
service_waiter_model = self._session.get_waiter_model(
|
||||
self._service_name)
|
||||
waiter_documenter = WaiterDocumenter(
|
||||
self._client, service_waiter_model)
|
||||
waiter_documenter.document_waiters(section)
|
||||
|
||||
def get_examples(self, service_name, api_version=None):
|
||||
loader = self._session.get_component('data_loader')
|
||||
examples = loader.load_service_model(
|
||||
service_name, 'examples-1', api_version)
|
||||
return examples['examples']
|
||||
@@ -0,0 +1,117 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
|
||||
|
||||
# NOTE: This class should not be instantiated and its
|
||||
# ``traverse_and_document_shape`` method called directly. It should be
|
||||
# inherited from a Documenter class with the appropriate methods
|
||||
# and attributes.
|
||||
from botocore.utils import is_json_value_header
|
||||
|
||||
|
||||
class ShapeDocumenter(object):
|
||||
EVENT_NAME = ''
|
||||
|
||||
def __init__(self, service_name, operation_name, event_emitter,
|
||||
context=None):
|
||||
self._service_name = service_name
|
||||
self._operation_name = operation_name
|
||||
self._event_emitter = event_emitter
|
||||
self._context = context
|
||||
if context is None:
|
||||
self._context = {
|
||||
'special_shape_types': {}
|
||||
}
|
||||
|
||||
def traverse_and_document_shape(self, section, shape, history,
|
||||
include=None, exclude=None, name=None,
|
||||
is_required=False):
|
||||
"""Traverses and documents a shape
|
||||
|
||||
Will take a self class and call its appropriate methods as a shape
|
||||
is traversed.
|
||||
|
||||
:param section: The section to document.
|
||||
|
||||
:param history: A list of the names of the shapes that have been
|
||||
traversed.
|
||||
|
||||
:type include: Dictionary where keys are parameter names and
|
||||
values are the shapes of the parameter names.
|
||||
:param include: The parameter shapes to include in the documentation.
|
||||
|
||||
:type exclude: List of the names of the parameters to exclude.
|
||||
:param exclude: The names of the parameters to exclude from
|
||||
documentation.
|
||||
|
||||
:param name: The name of the shape.
|
||||
|
||||
:param is_required: If the shape is a required member.
|
||||
"""
|
||||
param_type = shape.type_name
|
||||
if getattr(shape, 'serialization', {}).get('eventstream'):
|
||||
param_type = 'event_stream'
|
||||
if shape.name in history:
|
||||
self.document_recursive_shape(section, shape, name=name)
|
||||
else:
|
||||
history.append(shape.name)
|
||||
is_top_level_param = (len(history) == 2)
|
||||
getattr(self, 'document_shape_type_%s' % param_type,
|
||||
self.document_shape_default)(
|
||||
section, shape, history=history, name=name,
|
||||
include=include, exclude=exclude,
|
||||
is_top_level_param=is_top_level_param,
|
||||
is_required=is_required)
|
||||
if is_top_level_param:
|
||||
self._event_emitter.emit(
|
||||
'docs.%s.%s.%s.%s' % (self.EVENT_NAME,
|
||||
self._service_name,
|
||||
self._operation_name,
|
||||
name),
|
||||
section=section)
|
||||
at_overlying_method_section = (len(history) == 1)
|
||||
if at_overlying_method_section:
|
||||
self._event_emitter.emit(
|
||||
'docs.%s.%s.%s.complete-section' % (self.EVENT_NAME,
|
||||
self._service_name,
|
||||
self._operation_name),
|
||||
section=section)
|
||||
history.pop()
|
||||
|
||||
def _get_special_py_default(self, shape):
|
||||
special_defaults = {
|
||||
'jsonvalue_header': '{...}|[...]|123|123.4|\'string\'|True|None',
|
||||
'streaming_input_shape': 'b\'bytes\'|file',
|
||||
'streaming_output_shape': 'StreamingBody()',
|
||||
'eventstream_output_shape': 'EventStream()',
|
||||
}
|
||||
return self._get_value_for_special_type(shape, special_defaults)
|
||||
|
||||
def _get_special_py_type_name(self, shape):
|
||||
special_type_names = {
|
||||
'jsonvalue_header': 'JSON serializable',
|
||||
'streaming_input_shape': 'bytes or seekable file-like object',
|
||||
'streaming_output_shape': ':class:`.StreamingBody`',
|
||||
'eventstream_output_shape': ':class:`.EventStream`',
|
||||
}
|
||||
return self._get_value_for_special_type(shape, special_type_names)
|
||||
|
||||
def _get_value_for_special_type(self, shape, special_type_map):
|
||||
if is_json_value_header(shape):
|
||||
return special_type_map['jsonvalue_header']
|
||||
for special_type, marked_shape in self._context[
|
||||
'special_shape_types'].items():
|
||||
if special_type in special_type_map:
|
||||
if shape == marked_shape:
|
||||
return special_type_map[special_type]
|
||||
return None
|
||||
@@ -0,0 +1,223 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import re
|
||||
import numbers
|
||||
from botocore.utils import parse_timestamp
|
||||
from botocore.docs.utils import escape_controls
|
||||
from botocore.compat import six
|
||||
|
||||
|
||||
class SharedExampleDocumenter(object):
|
||||
def document_shared_example(self, example, prefix, section,
|
||||
operation_model):
|
||||
"""Documents a single shared example based on its definition.
|
||||
|
||||
:param example: The model of the example
|
||||
|
||||
:param prefix: The prefix to use in the method example.
|
||||
|
||||
:param section: The section to write to.
|
||||
|
||||
:param operation_model: The model of the operation used in the example
|
||||
"""
|
||||
section.style.new_paragraph()
|
||||
section.write(example.get('description'))
|
||||
section.style.new_line()
|
||||
self.document_input(section, example, prefix,
|
||||
operation_model.input_shape)
|
||||
self.document_output(section, example, operation_model.output_shape)
|
||||
|
||||
def document_input(self, section, example, prefix, shape):
|
||||
input_section = section.add_new_section('input')
|
||||
input_section.style.start_codeblock()
|
||||
if prefix is not None:
|
||||
input_section.write(prefix)
|
||||
params = example.get('input', {})
|
||||
comments = example.get('comments')
|
||||
if comments:
|
||||
comments = comments.get('input')
|
||||
param_section = input_section.add_new_section('parameters')
|
||||
self._document_params(param_section, params, comments, [], shape)
|
||||
closing_section = input_section.add_new_section('input-close')
|
||||
closing_section.style.new_line()
|
||||
closing_section.style.new_line()
|
||||
closing_section.write('print(response)')
|
||||
closing_section.style.end_codeblock()
|
||||
|
||||
def document_output(self, section, example, shape):
|
||||
output_section = section.add_new_section('output')
|
||||
output_section.style.new_line()
|
||||
output_section.write('Expected Output:')
|
||||
output_section.style.new_line()
|
||||
output_section.style.start_codeblock()
|
||||
params = example.get('output', {})
|
||||
|
||||
# There might not be an output, but we will return metadata anyway
|
||||
params['ResponseMetadata'] = {"...": "..."}
|
||||
comments = example.get('comments')
|
||||
if comments:
|
||||
comments = comments.get('output')
|
||||
self._document_dict(output_section, params, comments, [], shape, True)
|
||||
closing_section = output_section.add_new_section('output-close')
|
||||
closing_section.style.end_codeblock()
|
||||
|
||||
def _document(self, section, value, comments, path, shape):
|
||||
"""
|
||||
:param section: The section to add the docs to.
|
||||
|
||||
:param value: The input / output values representing the parameters that
|
||||
are included in the example.
|
||||
|
||||
:param comments: The dictionary containing all the comments to be
|
||||
applied to the example.
|
||||
|
||||
:param path: A list describing where the documenter is in traversing the
|
||||
parameters. This is used to find the equivalent location
|
||||
in the comments dictionary.
|
||||
"""
|
||||
if isinstance(value, dict):
|
||||
self._document_dict(section, value, comments, path, shape)
|
||||
elif isinstance(value, list):
|
||||
self._document_list(section, value, comments, path, shape)
|
||||
elif isinstance(value, numbers.Number):
|
||||
self._document_number(section, value, path)
|
||||
elif shape and shape.type_name == 'timestamp':
|
||||
self._document_datetime(section, value, path)
|
||||
else:
|
||||
self._document_str(section, value, path)
|
||||
|
||||
def _document_dict(self, section, value, comments, path, shape,
|
||||
top_level=False):
|
||||
dict_section = section.add_new_section('dict-value')
|
||||
self._start_nested_value(dict_section, '{')
|
||||
for key, val in value.items():
|
||||
path.append('.%s' % key)
|
||||
item_section = dict_section.add_new_section(key)
|
||||
item_section.style.new_line()
|
||||
item_comment = self._get_comment(path, comments)
|
||||
if item_comment:
|
||||
item_section.write(item_comment)
|
||||
item_section.style.new_line()
|
||||
item_section.write("'%s': " % key)
|
||||
|
||||
# Shape could be none if there is no output besides ResponseMetadata
|
||||
item_shape = None
|
||||
if shape:
|
||||
if shape.type_name == 'structure':
|
||||
item_shape = shape.members.get(key)
|
||||
elif shape.type_name == 'map':
|
||||
item_shape = shape.value
|
||||
self._document(item_section, val, comments, path, item_shape)
|
||||
path.pop()
|
||||
dict_section_end = dict_section.add_new_section('ending-brace')
|
||||
self._end_nested_value(dict_section_end, '}')
|
||||
if not top_level:
|
||||
dict_section_end.write(',')
|
||||
|
||||
def _document_params(self, section, value, comments, path, shape):
|
||||
param_section = section.add_new_section('param-values')
|
||||
self._start_nested_value(param_section, '(')
|
||||
for key, val in value.items():
|
||||
path.append('.%s' % key)
|
||||
item_section = param_section.add_new_section(key)
|
||||
item_section.style.new_line()
|
||||
item_comment = self._get_comment(path, comments)
|
||||
if item_comment:
|
||||
item_section.write(item_comment)
|
||||
item_section.style.new_line()
|
||||
item_section.write(key + '=')
|
||||
|
||||
# Shape could be none if there are no input parameters
|
||||
item_shape = None
|
||||
if shape:
|
||||
item_shape = shape.members.get(key)
|
||||
self._document(item_section, val, comments, path, item_shape)
|
||||
path.pop()
|
||||
param_section_end = param_section.add_new_section('ending-parenthesis')
|
||||
self._end_nested_value(param_section_end, ')')
|
||||
|
||||
def _document_list(self, section, value, comments, path, shape):
|
||||
list_section = section.add_new_section('list-section')
|
||||
self._start_nested_value(list_section, '[')
|
||||
item_shape = shape.member
|
||||
for index, val in enumerate(value):
|
||||
item_section = list_section.add_new_section(index)
|
||||
item_section.style.new_line()
|
||||
path.append('[%s]' % index)
|
||||
item_comment = self._get_comment(path, comments)
|
||||
if item_comment:
|
||||
item_section.write(item_comment)
|
||||
item_section.style.new_line()
|
||||
self._document(item_section, val, comments, path, item_shape)
|
||||
path.pop()
|
||||
list_section_end = list_section.add_new_section('ending-bracket')
|
||||
self._end_nested_value(list_section_end, '],')
|
||||
|
||||
def _document_str(self, section, value, path):
|
||||
# We do the string conversion because this might accept a type that
|
||||
# we don't specifically address.
|
||||
safe_value = escape_controls(value)
|
||||
section.write(u"'%s'," % six.text_type(safe_value))
|
||||
|
||||
def _document_number(self, section, value, path):
|
||||
section.write("%s," % str(value))
|
||||
|
||||
def _document_datetime(self, section, value, path):
|
||||
datetime_tuple = parse_timestamp(value).timetuple()
|
||||
datetime_str = str(datetime_tuple[0])
|
||||
for i in range(1, len(datetime_tuple)):
|
||||
datetime_str += ", " + str(datetime_tuple[i])
|
||||
section.write("datetime(%s)," % datetime_str)
|
||||
|
||||
def _get_comment(self, path, comments):
|
||||
key = re.sub(r'^\.', '', ''.join(path))
|
||||
if comments and key in comments:
|
||||
return '# ' + comments[key]
|
||||
else:
|
||||
return ''
|
||||
|
||||
def _start_nested_value(self, section, start):
|
||||
section.write(start)
|
||||
section.style.indent()
|
||||
section.style.indent()
|
||||
|
||||
def _end_nested_value(self, section, end):
|
||||
section.style.dedent()
|
||||
section.style.dedent()
|
||||
section.style.new_line()
|
||||
section.write(end)
|
||||
|
||||
|
||||
def document_shared_examples(section, operation_model, example_prefix,
|
||||
shared_examples):
|
||||
"""Documents the shared examples
|
||||
|
||||
:param section: The section to write to.
|
||||
|
||||
:param operation_model: The model of the operation.
|
||||
|
||||
:param example_prefix: The prefix to use in the method example.
|
||||
|
||||
:param shared_examples: The shared JSON examples from the model.
|
||||
"""
|
||||
container_section = section.add_new_section('shared-examples')
|
||||
container_section.style.new_paragraph()
|
||||
container_section.style.bold('Examples')
|
||||
documenter = SharedExampleDocumenter()
|
||||
for example in shared_examples:
|
||||
documenter.document_shared_example(
|
||||
example=example,
|
||||
section=container_section.add_new_section(example['id']),
|
||||
prefix=example_prefix,
|
||||
operation_model=operation_model
|
||||
)
|
||||
@@ -0,0 +1,197 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
def py_type_name(type_name):
|
||||
"""Get the Python type name for a given model type.
|
||||
|
||||
>>> py_type_name('list')
|
||||
'list'
|
||||
>>> py_type_name('structure')
|
||||
'dict'
|
||||
|
||||
:rtype: string
|
||||
"""
|
||||
return {
|
||||
'blob': 'bytes',
|
||||
'character': 'string',
|
||||
'double': 'float',
|
||||
'long': 'integer',
|
||||
'map': 'dict',
|
||||
'structure': 'dict',
|
||||
'timestamp': 'datetime',
|
||||
}.get(type_name, type_name)
|
||||
|
||||
|
||||
def py_default(type_name):
|
||||
"""Get the Python default value for a given model type.
|
||||
|
||||
>>> py_default('string')
|
||||
'\'string\''
|
||||
>>> py_default('list')
|
||||
'[...]'
|
||||
>>> py_default('unknown')
|
||||
'...'
|
||||
|
||||
:rtype: string
|
||||
"""
|
||||
return {
|
||||
'double': '123.0',
|
||||
'long': '123',
|
||||
'integer': '123',
|
||||
'string': "'string'",
|
||||
'blob': "b'bytes'",
|
||||
'boolean': 'True|False',
|
||||
'list': '[...]',
|
||||
'map': '{...}',
|
||||
'structure': '{...}',
|
||||
'timestamp': 'datetime(2015, 1, 1)',
|
||||
}.get(type_name, '...')
|
||||
|
||||
|
||||
def get_official_service_name(service_model):
|
||||
"""Generate the official name of an AWS Service
|
||||
|
||||
:param service_model: The service model representing the service
|
||||
"""
|
||||
official_name = service_model.metadata.get('serviceFullName')
|
||||
short_name = service_model.metadata.get('serviceAbbreviation', '')
|
||||
if short_name.startswith('Amazon'):
|
||||
short_name = short_name[7:]
|
||||
if short_name.startswith('AWS'):
|
||||
short_name = short_name[4:]
|
||||
if short_name and short_name.lower() not in official_name.lower():
|
||||
official_name += ' ({0})'.format(short_name)
|
||||
return official_name
|
||||
|
||||
|
||||
_DocumentedShape = namedtuple(
|
||||
'DocumentedShape', ['name', 'type_name', 'documentation', 'metadata',
|
||||
'members', 'required_members'])
|
||||
|
||||
|
||||
class DocumentedShape (_DocumentedShape):
|
||||
"""Use this class to inject new shapes into a model for documentation"""
|
||||
def __new__(cls, name, type_name, documentation, metadata=None,
|
||||
members=None, required_members=None):
|
||||
if metadata is None:
|
||||
metadata = []
|
||||
if members is None:
|
||||
members = []
|
||||
if required_members is None:
|
||||
required_members = []
|
||||
return super(DocumentedShape, cls).__new__(
|
||||
cls, name, type_name, documentation, metadata, members,
|
||||
required_members)
|
||||
|
||||
|
||||
class AutoPopulatedParam(object):
|
||||
def __init__(self, name, param_description=None):
|
||||
self.name = name
|
||||
self.param_description = param_description
|
||||
if param_description is None:
|
||||
self.param_description = (
|
||||
'Please note that this parameter is automatically populated '
|
||||
'if it is not provided. Including this parameter is not '
|
||||
'required\n')
|
||||
|
||||
def document_auto_populated_param(self, event_name, section, **kwargs):
|
||||
"""Documents auto populated parameters
|
||||
|
||||
It will remove any required marks for the parameter, remove the
|
||||
parameter from the example, and add a snippet about the parameter
|
||||
being autopopulated in the description.
|
||||
"""
|
||||
if event_name.startswith('docs.request-params'):
|
||||
if self.name in section.available_sections:
|
||||
section = section.get_section(self.name)
|
||||
if 'is-required' in section.available_sections:
|
||||
section.delete_section('is-required')
|
||||
description_section = section.get_section(
|
||||
'param-documentation')
|
||||
description_section.writeln(self.param_description)
|
||||
elif event_name.startswith('docs.request-example'):
|
||||
section = section.get_section('structure-value')
|
||||
if self.name in section.available_sections:
|
||||
section.delete_section(self.name)
|
||||
|
||||
|
||||
class HideParamFromOperations(object):
|
||||
"""Hides a single parameter from multiple operations.
|
||||
|
||||
This method will remove a parameter from documentation and from
|
||||
examples. This method is typically used for things that are
|
||||
automatically populated because a user would be unable to provide
|
||||
a value (e.g., a checksum of a serialized XML request body)."""
|
||||
def __init__(self, service_name, parameter_name, operation_names):
|
||||
"""
|
||||
:type service_name: str
|
||||
:param service_name: Name of the service to modify.
|
||||
|
||||
:type parameter_name: str
|
||||
:param parameter_name: Name of the parameter to modify.
|
||||
|
||||
:type operation_names: list
|
||||
:param operation_names: Operation names to modify.
|
||||
"""
|
||||
self._parameter_name = parameter_name
|
||||
self._params_events = set()
|
||||
self._example_events = set()
|
||||
# Build up the sets of relevant event names.
|
||||
param_template = 'docs.request-params.%s.%s.complete-section'
|
||||
example_template = 'docs.request-example.%s.%s.complete-section'
|
||||
for name in operation_names:
|
||||
self._params_events.add(param_template % (service_name, name))
|
||||
self._example_events.add(example_template % (service_name, name))
|
||||
|
||||
def hide_param(self, event_name, section, **kwargs):
|
||||
if event_name in self._example_events:
|
||||
# Modify the structure value for example events.
|
||||
section = section.get_section('structure-value')
|
||||
elif event_name not in self._params_events:
|
||||
return
|
||||
if self._parameter_name in section.available_sections:
|
||||
section.delete_section(self._parameter_name)
|
||||
|
||||
|
||||
class AppendParamDocumentation(object):
|
||||
"""Appends documentation to a specific parameter"""
|
||||
def __init__(self, parameter_name, doc_string):
|
||||
self._parameter_name = parameter_name
|
||||
self._doc_string = doc_string
|
||||
|
||||
def append_documentation(self, event_name, section, **kwargs):
|
||||
if self._parameter_name in section.available_sections:
|
||||
section = section.get_section(self._parameter_name)
|
||||
description_section = section.get_section(
|
||||
'param-documentation')
|
||||
description_section.writeln(self._doc_string)
|
||||
|
||||
|
||||
_CONTROLS = {
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
'\b': '\\b',
|
||||
'\f': '\\f',
|
||||
}
|
||||
# Combines all CONTROLS keys into a big or regular expression
|
||||
_ESCAPE_CONTROLS_RE = re.compile('|'.join(map(re.escape, _CONTROLS)))
|
||||
# Based on the match get the appropriate replacement from CONTROLS
|
||||
_CONTROLS_MATCH_HANDLER = lambda match: _CONTROLS[match.group(0)]
|
||||
|
||||
|
||||
def escape_controls(value):
|
||||
return _ESCAPE_CONTROLS_RE.sub(_CONTROLS_MATCH_HANDLER, value)
|
||||
@@ -0,0 +1,127 @@
|
||||
# Copyright 2015 Amazon.com, Inc. or its affiliates. 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. A copy of
|
||||
# the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "license" file accompanying this file. This file 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.
|
||||
from botocore import xform_name
|
||||
from botocore.compat import OrderedDict
|
||||
from botocore.docs.utils import DocumentedShape
|
||||
from botocore.utils import get_service_module_name
|
||||
from botocore.docs.method import document_model_driven_method
|
||||
|
||||
|
||||
class WaiterDocumenter(object):
|
||||
def __init__(self, client, service_waiter_model):
|
||||
self._client = client
|
||||
self._service_name = self._client.meta.service_model.service_name
|
||||
self._service_waiter_model = service_waiter_model
|
||||
|
||||
def document_waiters(self, section):
|
||||
"""Documents the various waiters for a service.
|
||||
|
||||
:param section: The section to write to.
|
||||
"""
|
||||
section.style.h2('Waiters')
|
||||
section.style.new_line()
|
||||
section.writeln('The available waiters are:')
|
||||
for waiter_name in self._service_waiter_model.waiter_names:
|
||||
section.style.li(
|
||||
':py:class:`%s.Waiter.%s`' % (
|
||||
self._client.__class__.__name__, waiter_name))
|
||||
self._add_single_waiter(section, waiter_name)
|
||||
|
||||
def _add_single_waiter(self, section, waiter_name):
|
||||
section = section.add_new_section(waiter_name)
|
||||
section.style.start_sphinx_py_class(
|
||||
class_name='%s.Waiter.%s' % (
|
||||
self._client.__class__.__name__, waiter_name))
|
||||
|
||||
# Add example on how to instantiate waiter.
|
||||
section.style.start_codeblock()
|
||||
section.style.new_line()
|
||||
section.write(
|
||||
'waiter = client.get_waiter(\'%s\')' % xform_name(waiter_name)
|
||||
)
|
||||
section.style.end_codeblock()
|
||||
|
||||
# Add information on the wait() method
|
||||
section.style.new_line()
|
||||
document_wait_method(
|
||||
section=section,
|
||||
waiter_name=waiter_name,
|
||||
event_emitter=self._client.meta.events,
|
||||
service_model=self._client.meta.service_model,
|
||||
service_waiter_model=self._service_waiter_model
|
||||
)
|
||||
|
||||
|
||||
def document_wait_method(section, waiter_name, event_emitter,
|
||||
service_model, service_waiter_model,
|
||||
include_signature=True):
|
||||
"""Documents a the wait method of a waiter
|
||||
|
||||
:param section: The section to write to
|
||||
|
||||
:param waiter_name: The name of the waiter
|
||||
|
||||
:param event_emitter: The event emitter to use to emit events
|
||||
|
||||
:param service_model: The service model
|
||||
|
||||
:param service_waiter_model: The waiter model associated to the service
|
||||
|
||||
:param include_signature: Whether or not to include the signature.
|
||||
It is useful for generating docstrings.
|
||||
"""
|
||||
waiter_model = service_waiter_model.get_waiter(waiter_name)
|
||||
operation_model = service_model.operation_model(
|
||||
waiter_model.operation)
|
||||
|
||||
waiter_config_members = OrderedDict()
|
||||
|
||||
waiter_config_members['Delay'] = DocumentedShape(
|
||||
name='Delay', type_name='integer',
|
||||
documentation=(
|
||||
'<p>The amount of time in seconds to wait between '
|
||||
'attempts. Default: {0}</p>'.format(waiter_model.delay)))
|
||||
|
||||
waiter_config_members['MaxAttempts'] = DocumentedShape(
|
||||
name='MaxAttempts', type_name='integer',
|
||||
documentation=(
|
||||
'<p>The maximum number of attempts to be made. '
|
||||
'Default: {0}</p>'.format(waiter_model.max_attempts)))
|
||||
|
||||
botocore_waiter_params = [
|
||||
DocumentedShape(
|
||||
name='WaiterConfig', type_name='structure',
|
||||
documentation=(
|
||||
'<p>A dictionary that provides parameters to control '
|
||||
'waiting behavior.</p>'),
|
||||
members=waiter_config_members)
|
||||
]
|
||||
|
||||
wait_description = (
|
||||
'Polls :py:meth:`{0}.Client.{1}` every {2} '
|
||||
'seconds until a successful state is reached. An error is '
|
||||
'returned after {3} failed checks.'.format(
|
||||
get_service_module_name(service_model),
|
||||
xform_name(waiter_model.operation),
|
||||
waiter_model.delay, waiter_model.max_attempts)
|
||||
)
|
||||
|
||||
document_model_driven_method(
|
||||
section, 'wait', operation_model,
|
||||
event_emitter=event_emitter,
|
||||
method_description=wait_description,
|
||||
example_prefix='waiter.wait',
|
||||
include_input=botocore_waiter_params,
|
||||
document_output=False,
|
||||
include_signature=include_signature
|
||||
)
|
||||
Reference in New Issue
Block a user