feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
# 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 boto3.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
:param session: The boto3 session
"""
services_doc_path = os.path.join(root_dir, 'reference', 'services')
if not os.path.exists(services_doc_path):
os.makedirs(services_doc_path)
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)

View File

@@ -0,0 +1,148 @@
# 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.model import OperationModel
from botocore.utils import get_service_module_name
from botocore.docs.method import document_model_driven_method
from botocore.docs.method import document_custom_method
from boto3.docs.base import BaseDocumenter
from boto3.docs.method import document_model_driven_resource_method
from boto3.docs.utils import get_resource_ignore_params
from boto3.docs.utils import get_resource_public_actions
from boto3.docs.utils import add_resource_type_overview
class ActionDocumenter(BaseDocumenter):
def document_actions(self, section):
modeled_actions_list = self._resource_model.actions
modeled_actions = {}
for modeled_action in modeled_actions_list:
modeled_actions[modeled_action.name] = modeled_action
resource_actions = get_resource_public_actions(
self._resource.__class__)
self.member_map['actions'] = sorted(resource_actions)
add_resource_type_overview(
section=section,
resource_type='Actions',
description=(
'Actions call operations on resources. They may '
'automatically handle the passing in of arguments set '
'from identifiers and some attributes.'),
intro_link='actions_intro')
for action_name in sorted(resource_actions):
action_section = section.add_new_section(action_name)
if action_name in ['load', 'reload'] and self._resource_model.load:
document_load_reload_action(
section=action_section,
action_name=action_name,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
load_model=self._resource_model.load,
service_model=self._service_model
)
elif action_name in modeled_actions:
document_action(
section=action_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
action_model=modeled_actions[action_name],
service_model=self._service_model,
)
else:
document_custom_method(
action_section, action_name, resource_actions[action_name])
def document_action(section, resource_name, event_emitter, action_model,
service_model, include_signature=True):
"""Documents a resource action
:param section: The section to write to
:param resource_name: The name of the resource
:param event_emitter: The event emitter to use to emit events
:param action_model: The model of the action
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
action_model.request.operation)
ignore_params = get_resource_ignore_params(action_model.request.params)
example_return_value = 'response'
if action_model.resource:
example_return_value = xform_name(action_model.resource.type)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = '%s = %s.%s' % (
example_return_value, example_resource_name, action_model.name)
document_model_driven_resource_method(
section=section, method_name=action_model.name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=operation_model.documentation,
example_prefix=example_prefix,
exclude_input=ignore_params,
resource_action_model=action_model,
include_signature=include_signature
)
def document_load_reload_action(section, action_name, resource_name,
event_emitter, load_model, service_model,
include_signature=True):
"""Documents the resource load action
:param section: The section to write to
:param action_name: The name of the loading action should be load or reload
:param resource_name: The name of the resource
:param event_emitter: The event emitter to use to emit events
:param load_model: The model of the load action
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
description = (
'Calls :py:meth:`%s.Client.%s` to update the attributes of the'
' %s resource. Note that the load and reload methods are '
'the same method and can be used interchangeably.' % (
get_service_module_name(service_model),
xform_name(load_model.request.operation),
resource_name)
)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = '%s.%s' % (example_resource_name, action_name)
document_model_driven_method(
section=section, method_name=action_name,
operation_model=OperationModel({}, service_model),
event_emitter=event_emitter,
method_description=description,
example_prefix=example_prefix,
include_signature=include_signature
)

View File

@@ -0,0 +1,54 @@
# 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.params import ResponseParamsDocumenter
from boto3.docs.utils import get_identifier_description
class ResourceShapeDocumenter(ResponseParamsDocumenter):
EVENT_NAME = 'resource-shape'
def document_attribute(section, service_name, resource_name, attr_name,
event_emitter, attr_model, include_signature=True):
if include_signature:
section.style.start_sphinx_py_attr(attr_name)
# Note that an attribute may have one, may have many, or may have no
# operations that back the resource's shape. So we just set the
# operation_name to the resource name if we ever to hook in and modify
# a particular attribute.
ResourceShapeDocumenter(
service_name=service_name, operation_name=resource_name,
event_emitter=event_emitter).document_params(
section=section,
shape=attr_model)
def document_identifier(section, resource_name, identifier_model,
include_signature=True):
if include_signature:
section.style.start_sphinx_py_attr(identifier_model.name)
description = get_identifier_description(
resource_name, identifier_model.name)
description = '*(string)* ' + description
section.write(description)
def document_reference(section, reference_model, include_signature=True):
if include_signature:
section.style.start_sphinx_py_attr(reference_model.name)
reference_type = '(:py:class:`%s`) ' % reference_model.resource.type
section.write(reference_type)
section.include_doc_string(
'The related %s if set, otherwise ``None``.' % reference_model.name
)

View File

@@ -0,0 +1,31 @@
# 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.compat import OrderedDict
class BaseDocumenter(object):
def __init__(self, resource):
self._resource = resource
self._client = self._resource.meta.client
self._resource_model = self._resource.meta.resource_model
self._service_model = self._client.meta.service_model
self._resource_name = self._resource.meta.resource_model.name
self._service_name = self._service_model.service_name
self._service_docs_name = self._client.__class__.__name__
self.member_map = OrderedDict()
self.represents_service_resource = (
self._service_name == self._resource_name)
@property
def class_name(self):
return '%s.%s' % (self._service_docs_name, self._resource_name)

View File

@@ -0,0 +1,27 @@
# 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.client import ClientDocumenter
class Boto3ClientDocumenter(ClientDocumenter):
def _add_client_creation_example(self, section):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
'client = boto3.client(\'{service}\')'.format(
service=self._service_name)
)
section.style.end_codeblock()

View File

@@ -0,0 +1,242 @@
# 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.docs.method import get_instance_public_methods
from botocore.docs.utils import DocumentedShape
from boto3.docs.base import BaseDocumenter
from boto3.docs.utils import get_resource_ignore_params
from boto3.docs.method import document_model_driven_resource_method
from boto3.docs.utils import add_resource_type_overview
class CollectionDocumenter(BaseDocumenter):
def document_collections(self, section):
collections = self._resource.meta.resource_model.collections
collections_list = []
add_resource_type_overview(
section=section,
resource_type='Collections',
description=(
'Collections provide an interface to iterate over and '
'manipulate groups of resources. '),
intro_link='guide_collections')
self.member_map['collections'] = collections_list
for collection in collections:
collection_section = section.add_new_section(collection.name)
collections_list.append(collection.name)
self._document_collection(collection_section, collection)
def _document_collection(self, section, collection):
methods = get_instance_public_methods(
getattr(self._resource, collection.name))
document_collection_object(section, collection)
batch_actions = {}
for batch_action in collection.batch_actions:
batch_actions[batch_action.name] = batch_action
for method in sorted(methods):
method_section = section.add_new_section(method)
if method in batch_actions:
document_batch_action(
section=method_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
batch_action_model=batch_actions[method],
collection_model=collection,
service_model=self._resource.meta.client.meta.service_model
)
else:
document_collection_method(
section=method_section,
resource_name=self._resource_name,
action_name=method,
event_emitter=self._resource.meta.client.meta.events,
collection_model=collection,
service_model=self._resource.meta.client.meta.service_model
)
def document_collection_object(section, collection_model,
include_signature=True):
"""Documents a collection resource object
:param section: The section to write to
:param collection_model: The model of the collection
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
if include_signature:
section.style.start_sphinx_py_attr(collection_model.name)
section.include_doc_string(
'A collection of %s resources.' % collection_model.resource.type)
section.include_doc_string(
'A %s Collection will include all resources by default, '
'and extreme caution should be taken when performing '
'actions on all resources.' % collection_model.resource.type)
def document_batch_action(section, resource_name, event_emitter,
batch_action_model, service_model, collection_model,
include_signature=True):
"""Documents a collection's batch action
:param section: The section to write to
:param resource_name: The name of the resource
:param action_name: The name of collection action. Currently only
can be all, filter, limit, or page_size
:param event_emitter: The event emitter to use to emit events
:param batch_action_model: The model of the batch action
:param collection_model: The model of the collection
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
batch_action_model.request.operation)
ignore_params = get_resource_ignore_params(
batch_action_model.request.params)
example_return_value = 'response'
if batch_action_model.resource:
example_return_value = xform_name(batch_action_model.resource.type)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = '%s = %s.%s.%s' % (
example_return_value, example_resource_name,
collection_model.name, batch_action_model.name
)
document_model_driven_resource_method(
section=section, method_name=batch_action_model.name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=operation_model.documentation,
example_prefix=example_prefix,
exclude_input=ignore_params,
resource_action_model=batch_action_model,
include_signature=include_signature
)
def document_collection_method(section, resource_name, action_name,
event_emitter, collection_model, service_model,
include_signature=True):
"""Documents a collection method
:param section: The section to write to
:param resource_name: The name of the resource
:param action_name: The name of collection action. Currently only
can be all, filter, limit, or page_size
:param event_emitter: The event emitter to use to emit events
:param collection_model: The model of the collection
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
collection_model.request.operation)
underlying_operation_members = []
if operation_model.input_shape:
underlying_operation_members = operation_model.input_shape.members
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
custom_action_info_dict = {
'all': {
'method_description': (
'Creates an iterable of all %s resources '
'in the collection.' % collection_model.resource.type),
'example_prefix': '%s_iterator = %s.%s.all' % (
xform_name(collection_model.resource.type),
example_resource_name, collection_model.name),
'exclude_input': underlying_operation_members
},
'filter': {
'method_description': (
'Creates an iterable of all %s resources '
'in the collection filtered by kwargs passed to '
'method.' % collection_model.resource.type +
'A %s collection will include all resources by '
'default if no filters are provided, and extreme '
'caution should be taken when performing actions '
'on all resources.'% collection_model.resource.type),
'example_prefix': '%s_iterator = %s.%s.filter' % (
xform_name(collection_model.resource.type),
example_resource_name, collection_model.name),
'exclude_input': get_resource_ignore_params(
collection_model.request.params)
},
'limit': {
'method_description': (
'Creates an iterable up to a specified amount of '
'%s resources in the collection.' %
collection_model.resource.type),
'example_prefix': '%s_iterator = %s.%s.limit' % (
xform_name(collection_model.resource.type),
example_resource_name, collection_model.name),
'include_input': [
DocumentedShape(
name='count', type_name='integer',
documentation=(
'The limit to the number of resources '
'in the iterable.'))],
'exclude_input': underlying_operation_members
},
'page_size': {
'method_description': (
'Creates an iterable of all %s resources '
'in the collection, but limits the number of '
'items returned by each service call by the specified '
'amount.' % collection_model.resource.type),
'example_prefix': '%s_iterator = %s.%s.page_size' % (
xform_name(collection_model.resource.type),
example_resource_name, collection_model.name),
'include_input': [
DocumentedShape(
name='count', type_name='integer',
documentation=(
'The number of items returned by each '
'service call'))],
'exclude_input': underlying_operation_members
}
}
if action_name in custom_action_info_dict:
action_info = custom_action_info_dict[action_name]
document_model_driven_resource_method(
section=section, method_name=action_name,
operation_model=operation_model,
event_emitter=event_emitter,
resource_action_model=collection_model,
include_signature=include_signature,
**action_info
)

View File

@@ -0,0 +1,74 @@
# 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.docstring import LazyLoadedDocstring
from boto3.docs.action import document_action
from boto3.docs.action import document_load_reload_action
from boto3.docs.subresource import document_sub_resource
from boto3.docs.attr import document_attribute
from boto3.docs.attr import document_identifier
from boto3.docs.attr import document_reference
from boto3.docs.collection import document_collection_object
from boto3.docs.collection import document_collection_method
from boto3.docs.collection import document_batch_action
from boto3.docs.waiter import document_resource_waiter
class ActionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_action(*args, **kwargs)
class LoadReloadDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_load_reload_action(*args, **kwargs)
class SubResourceDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_sub_resource(*args, **kwargs)
class AttributeDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_attribute(*args, **kwargs)
class IdentifierDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_identifier(*args, **kwargs)
class ReferenceDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_reference(*args, **kwargs)
class CollectionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_collection_object(*args, **kwargs)
class CollectionMethodDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_collection_method(*args, **kwargs)
class BatchActionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_batch_action(*args, **kwargs)
class ResourceWaiterDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_resource_waiter(*args, **kwargs)

View File

@@ -0,0 +1,71 @@
# 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
def document_model_driven_resource_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, resource_action_model=None,
include_signature=True):
document_model_driven_method(
section=section, method_name=method_name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=method_description,
example_prefix=example_prefix,
include_input=include_input,
include_output=include_output,
exclude_input=exclude_input,
exclude_output=exclude_output,
document_output=document_output,
include_signature=include_signature
)
# If this action returns a resource modify the return example to
# appropriately reflect that.
if resource_action_model.resource:
if 'return' in section.available_sections:
section.delete_section('return')
resource_type = resource_action_model.resource.type
new_return_section = section.add_new_section('return')
return_resource_type = '%s.%s' % (
operation_model.service_model.service_name,
resource_type)
return_type = ':py:class:`%s`' % return_resource_type
return_description = '%s resource' % (resource_type)
if _method_returns_resource_list(resource_action_model.resource):
return_type = 'list(%s)' % return_type
return_description = 'A list of %s resources' % (
resource_type)
new_return_section.style.new_line()
new_return_section.write(
':rtype: %s' % return_type)
new_return_section.style.new_line()
new_return_section.write(
':returns: %s' % return_description)
new_return_section.style.new_line()
def _method_returns_resource_list(resource):
for identifier in resource.identifiers:
if identifier.path and '[]' in identifier.path:
return True
return False

View File

@@ -0,0 +1,259 @@
# 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.docs.utils import get_official_service_name
from boto3.docs.base import BaseDocumenter
from boto3.docs.action import ActionDocumenter
from boto3.docs.waiter import WaiterResourceDocumenter
from boto3.docs.collection import CollectionDocumenter
from boto3.docs.subresource import SubResourceDocumenter
from boto3.docs.attr import document_attribute
from boto3.docs.attr import document_identifier
from boto3.docs.attr import document_reference
from boto3.docs.utils import get_identifier_args_for_signature
from boto3.docs.utils import get_identifier_values_for_example
from boto3.docs.utils import get_identifier_description
from boto3.docs.utils import add_resource_type_overview
class ResourceDocumenter(BaseDocumenter):
def __init__(self, resource, botocore_session):
super(ResourceDocumenter, self).__init__(resource)
self._botocore_session = botocore_session
def document_resource(self, section):
self._add_title(section)
self._add_intro(section)
overview_section = section.add_new_section('member-overview')
self._add_identifiers(section)
self._add_attributes(section)
self._add_references(section)
self._add_actions(section)
self._add_sub_resources(section)
self._add_collections(section)
self._add_waiters(section)
self._add_overview_of_members(overview_section)
def _add_title(self, section):
section.style.h2(self._resource_name)
def _add_intro(self, section):
identifier_names = []
if self._resource_model.identifiers:
for identifier in self._resource_model.identifiers:
identifier_names.append(identifier.name)
# Write out the class signature.
class_args = get_identifier_args_for_signature(identifier_names)
section.style.start_sphinx_py_class(
class_name='%s(%s)' % (self.class_name, class_args))
# Add as short description about the resource
description_section = section.add_new_section('description')
self._add_description(description_section)
# Add an example of how to instantiate the resource
example_section = section.add_new_section('example')
self._add_example(example_section, identifier_names)
# Add the description for the parameters to instantiate the
# resource.
param_section = section.add_new_section('params')
self._add_params_description(param_section, identifier_names)
def _add_description(self, section):
official_service_name = get_official_service_name(
self._service_model)
section.write(
'A resource representing an %s %s' % (
official_service_name, self._resource_name))
def _add_example(self, section, identifier_names):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
'%s = boto3.resource(\'%s\')' % (
self._service_name, self._service_name)
)
section.style.new_line()
example_values = get_identifier_values_for_example(identifier_names)
section.write(
'%s = %s.%s(%s)' % (
xform_name(self._resource_name), self._service_name,
self._resource_name, example_values))
section.style.end_codeblock()
def _add_params_description(self, section, identifier_names):
for identifier_name in identifier_names:
description = get_identifier_description(
self._resource_name, identifier_name)
section.write(':type %s: string' % identifier_name)
section.style.new_line()
section.write(':param %s: %s' % (
identifier_name, description))
section.style.new_line()
def _add_overview_of_members(self, section):
for resource_member_type in self.member_map:
section.style.new_line()
section.write('These are the resource\'s available %s:' % (
resource_member_type))
section.style.new_line()
for member in self.member_map[resource_member_type]:
if resource_member_type in ['identifiers', 'attributes',
'references', 'collections']:
section.style.li(':py:attr:`%s`' % member)
else:
section.style.li(':py:meth:`%s()`' % member)
def _add_identifiers(self, section):
identifiers = self._resource.meta.resource_model.identifiers
section = section.add_new_section('identifiers')
member_list = []
if identifiers:
self.member_map['identifiers'] = member_list
add_resource_type_overview(
section=section,
resource_type='Identifiers',
description=(
'Identifiers are properties of a resource that are '
'set upon instantation of the resource.'),
intro_link='identifiers_attributes_intro')
for identifier in identifiers:
identifier_section = section.add_new_section(identifier.name)
member_list.append(identifier.name)
document_identifier(
section=identifier_section,
resource_name=self._resource_name,
identifier_model=identifier
)
def _add_attributes(self, section):
service_model = self._resource.meta.client.meta.service_model
attributes = {}
if self._resource.meta.resource_model.shape:
shape = service_model.shape_for(
self._resource.meta.resource_model.shape)
attributes = self._resource.meta.resource_model.get_attributes(
shape)
section = section.add_new_section('attributes')
attribute_list = []
if attributes:
add_resource_type_overview(
section=section,
resource_type='Attributes',
description=(
'Attributes provide access'
' to the properties of a resource. Attributes are lazy-'
'loaded the first time one is accessed via the'
' :py:meth:`load` method.'),
intro_link='identifiers_attributes_intro')
self.member_map['attributes'] = attribute_list
for attr_name in sorted(attributes):
_, attr_shape = attributes[attr_name]
attribute_section = section.add_new_section(attr_name)
attribute_list.append(attr_name)
document_attribute(
section=attribute_section,
service_name=self._service_name,
resource_name=self._resource_name,
attr_name=attr_name,
event_emitter=self._resource.meta.client.meta.events,
attr_model=attr_shape
)
def _add_references(self, section):
section = section.add_new_section('references')
references = self._resource.meta.resource_model.references
reference_list = []
if references:
add_resource_type_overview(
section=section,
resource_type='References',
description=(
'References are related resource instances that have '
'a belongs-to relationship.'),
intro_link='references_intro')
self.member_map['references'] = reference_list
for reference in references:
reference_section = section.add_new_section(reference.name)
reference_list.append(reference.name)
document_reference(
section=reference_section,
reference_model=reference
)
def _add_actions(self, section):
section = section.add_new_section('actions')
actions = self._resource.meta.resource_model.actions
if actions:
documenter = ActionDocumenter(self._resource)
documenter.member_map = self.member_map
documenter.document_actions(section)
def _add_sub_resources(self, section):
section = section.add_new_section('sub-resources')
sub_resources = self._resource.meta.resource_model.subresources
if sub_resources:
documenter = SubResourceDocumenter(self._resource)
documenter.member_map = self.member_map
documenter.document_sub_resources(section)
def _add_collections(self, section):
section = section.add_new_section('collections')
collections = self._resource.meta.resource_model.collections
if collections:
documenter = CollectionDocumenter(self._resource)
documenter.member_map = self.member_map
documenter.document_collections(section)
def _add_waiters(self, section):
section = section.add_new_section('waiters')
waiters = self._resource.meta.resource_model.waiters
if waiters:
service_waiter_model = self._botocore_session.get_waiter_model(
self._service_name)
documenter = WaiterResourceDocumenter(
self._resource, service_waiter_model)
documenter.member_map = self.member_map
documenter.document_resource_waiters(section)
class ServiceResourceDocumenter(ResourceDocumenter):
@property
def class_name(self):
return '%s.ServiceResource' % self._service_docs_name
def _add_title(self, section):
section.style.h2('Service Resource')
def _add_description(self, section):
official_service_name = get_official_service_name(
self._service_model)
section.write(
'A resource representing %s' % official_service_name)
def _add_example(self, section, identifier_names):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
'%s = boto3.resource(\'%s\')' % (
self._service_name, self._service_name))
section.style.end_codeblock()

View File

@@ -0,0 +1,131 @@
# 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
import boto3
from botocore.exceptions import DataNotFoundError
from botocore.docs.service import ServiceDocumenter as BaseServiceDocumenter
from botocore.docs.bcdoc.restdoc import DocumentStructure
from boto3.utils import ServiceContext
from boto3.docs.client import Boto3ClientDocumenter
from boto3.docs.resource import ResourceDocumenter
from boto3.docs.resource import ServiceResourceDocumenter
class ServiceDocumenter(BaseServiceDocumenter):
# The path used to find examples
EXAMPLE_PATH = os.path.join(os.path.dirname(boto3.__file__), 'examples')
def __init__(self, service_name, session):
super(ServiceDocumenter, self).__init__(
service_name=service_name,
# I know that this is an internal attribute, but the botocore session
# is needed to load the paginator and waiter models.
session=session._session,
)
self._boto3_session = session
self._client = self._boto3_session.client(service_name)
self._service_resource = None
if self._service_name in self._boto3_session.get_available_resources():
self._service_resource = self._boto3_session.resource(service_name)
self.sections = [
'title',
'table-of-contents',
'client',
'paginators',
'waiters',
'service-resource',
'resources',
'examples'
]
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'))
self.paginator_api(doc_structure.get_section('paginators'))
self.waiter_api(doc_structure.get_section('waiters'))
if self._service_resource:
self._document_service_resource(
doc_structure.get_section('service-resource'))
self._document_resources(doc_structure.get_section('resources'))
self._document_examples(doc_structure.get_section('examples'))
return doc_structure.flush_structure()
def client_api(self, section):
examples = None
try:
examples = self.get_examples(self._service_name)
except DataNotFoundError:
pass
Boto3ClientDocumenter(self._client, examples).document_client(section)
def _document_service_resource(self, section):
ServiceResourceDocumenter(
self._service_resource, self._session).document_resource(
section)
def _document_resources(self, section):
temp_identifier_value = 'foo'
loader = self._session.get_component('data_loader')
json_resource_model = loader.load_service_model(
self._service_name, 'resources-1')
service_model = self._service_resource.meta.client.meta.service_model
for resource_name in json_resource_model['resources']:
resource_model = json_resource_model['resources'][resource_name]
resource_cls = self._boto3_session.resource_factory.\
load_from_definition(
resource_name=resource_name,
single_resource_json_definition=resource_model,
service_context=ServiceContext(
service_name=self._service_name,
resource_json_definitions=json_resource_model[
'resources'],
service_model=service_model,
service_waiter_model=None
)
)
identifiers = resource_cls.meta.resource_model.identifiers
args = []
for _ in identifiers:
args.append(temp_identifier_value)
resource = resource_cls(*args, client=self._client)
ResourceDocumenter(
resource, self._session).document_resource(
section.add_new_section(resource.meta.resource_model.name))
def _get_example_file(self):
return os.path.realpath(
os.path.join(self.EXAMPLE_PATH,
self._service_name + '.rst'))
def _document_examples(self, section):
examples_file = self._get_example_file()
if os.path.isfile(examples_file):
section.style.h2('Examples')
section.style.new_line()
section.write(".. contents::\n :local:\n :depth: 1")
section.style.new_line()
section.style.new_line()
with open(examples_file, 'r') as f:
section.write(f.read())

View File

@@ -0,0 +1,112 @@
# 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.utils import get_service_module_name
from boto3.docs.base import BaseDocumenter
from boto3.docs.utils import get_identifier_args_for_signature
from boto3.docs.utils import get_identifier_values_for_example
from boto3.docs.utils import get_identifier_description
from boto3.docs.utils import add_resource_type_overview
class SubResourceDocumenter(BaseDocumenter):
def document_sub_resources(self, section):
add_resource_type_overview(
section=section,
resource_type='Sub-resources',
description=(
'Sub-resources are methods that create a new instance of a'
' child resource. This resource\'s identifiers get passed'
' along to the child.'),
intro_link='subresources_intro')
sub_resources = sorted(
self._resource.meta.resource_model.subresources,
key=lambda sub_resource: sub_resource.name
)
sub_resources_list = []
self.member_map['sub-resources'] = sub_resources_list
for sub_resource in sub_resources:
sub_resource_section = section.add_new_section(sub_resource.name)
sub_resources_list.append(sub_resource.name)
document_sub_resource(
section=sub_resource_section,
resource_name=self._resource_name,
sub_resource_model=sub_resource,
service_model=self._service_model
)
def document_sub_resource(section, resource_name, sub_resource_model,
service_model, include_signature=True):
"""Documents a resource action
:param section: The section to write to
:param resource_name: The name of the resource
:param sub_resource_model: The model of the subresource
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
identifiers_needed = []
for identifier in sub_resource_model.resource.identifiers:
if identifier.source == 'input':
identifiers_needed.append(xform_name(identifier.target))
if include_signature:
signature_args = get_identifier_args_for_signature(identifiers_needed)
section.style.start_sphinx_py_method(
sub_resource_model.name, signature_args)
method_intro_section = section.add_new_section(
'method-intro')
description = 'Creates a %s resource.' % sub_resource_model.resource.type
method_intro_section.include_doc_string(description)
example_section = section.add_new_section('example')
example_values = get_identifier_values_for_example(identifiers_needed)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example = '%s = %s.%s(%s)' % (
xform_name(sub_resource_model.resource.type),
example_resource_name,
sub_resource_model.name, example_values
)
example_section.style.start_codeblock()
example_section.write(example)
example_section.style.end_codeblock()
param_section = section.add_new_section('params')
for identifier in identifiers_needed:
description = get_identifier_description(
sub_resource_model.name, identifier)
param_section.write(':type %s: string' % identifier)
param_section.style.new_line()
param_section.write(':param %s: %s' % (
identifier, description))
param_section.style.new_line()
return_section = section.add_new_section('return')
return_section.style.new_line()
return_section.write(
':rtype: :py:class:`%s.%s`' % (
get_service_module_name(service_model),
sub_resource_model.resource.type))
return_section.style.new_line()
return_section.write(
':returns: A %s resource' % sub_resource_model.resource.type)
return_section.style.new_line()

View File

@@ -0,0 +1,142 @@
# 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
import jmespath
from botocore.compat import six
def get_resource_ignore_params(params):
"""Helper method to determine which parameters to ignore for actions
:returns: A list of the parameter names that does not need to be
included in a resource's method call for documentation purposes.
"""
ignore_params = []
for param in params:
result = jmespath.compile(param.target)
current = result.parsed
# Use JMESPath to find the left most element in the target expression
# which will be the parameter to ignore in the action call.
while current['children']:
current = current['children'][0]
# Make sure the parameter we are about to ignore is a field.
# If it is not, we should ignore the result to avoid false positives.
if current['type'] == 'field':
ignore_params.append(current['value'])
return ignore_params
def is_resource_action(action_handle):
if six.PY3:
return inspect.isfunction(action_handle)
else:
return inspect.ismethod(action_handle)
def get_resource_public_actions(resource_class):
resource_class_members = inspect.getmembers(resource_class)
resource_methods = {}
for name, member in resource_class_members:
if not name.startswith('_'):
if not name[0].isupper():
if not name.startswith('wait_until'):
if is_resource_action(member):
resource_methods[name] = member
return resource_methods
def get_identifier_values_for_example(identifier_names):
example_values = ['\'%s\'' % identifier for identifier in identifier_names]
return ','.join(example_values)
def get_identifier_args_for_signature(identifier_names):
return ','.join(identifier_names)
def get_identifier_description(resource_name, identifier_name):
return "The %s's %s identifier. This **must** be set." % (
resource_name, identifier_name)
def add_resource_type_overview(section, resource_type, description,
intro_link=None):
section.style.new_line()
section.write('.. rst-class:: admonition-title')
section.style.new_line()
section.style.new_line()
section.write(resource_type)
section.style.new_line()
section.style.new_line()
section.write(description)
section.style.new_line()
if intro_link is not None:
section.write('For more information about %s refer to the '
':ref:`Resources Introduction Guide<%s>`.' % (
resource_type.lower(), intro_link))
section.style.new_line()
class DocumentModifiedShape(object):
def __init__(self, shape_name, new_type, new_description,
new_example_value):
self._shape_name = shape_name
self._new_type = new_type
self._new_description = new_description
self._new_example_value = new_example_value
def replace_documentation_for_matching_shape(self, event_name, section,
**kwargs):
if self._shape_name == section.context.get('shape'):
self._replace_documentation(event_name, section)
for section_name in section.available_sections:
sub_section = section.get_section(section_name)
if self._shape_name == sub_section.context.get('shape'):
self._replace_documentation(event_name, sub_section)
else:
self.replace_documentation_for_matching_shape(
event_name, sub_section)
def _replace_documentation(self, event_name, section):
if event_name.startswith('docs.request-example') or \
event_name.startswith('docs.response-example'):
section.remove_all_sections()
section.clear_text()
section.write(self._new_example_value)
if event_name.startswith('docs.request-params') or \
event_name.startswith('docs.response-params'):
for section_name in section.available_sections:
# Delete any extra members as a new shape is being
# used.
if section_name not in ['param-name', 'param-documentation',
'end-structure', 'param-type',
'end-param']:
section.delete_section(section_name)
# Update the documentation
description_section = section.get_section('param-documentation')
description_section.clear_text()
description_section.write(self._new_description)
# Update the param type
type_section = section.get_section('param-type')
if type_section.getvalue().decode('utf-8').startswith(':type'):
type_section.clear_text()
type_section.write(':type %s: %s' % (
section.name, self._new_type))
else:
type_section.clear_text()
type_section.style.italics('(%s) -- ' % self._new_type)

View File

@@ -0,0 +1,91 @@
# 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.utils import get_service_module_name
from botocore.docs.method import document_model_driven_method
from boto3.docs.base import BaseDocumenter
from boto3.docs.utils import get_resource_ignore_params
from boto3.docs.utils import add_resource_type_overview
class WaiterResourceDocumenter(BaseDocumenter):
def __init__(self, resource, service_waiter_model):
super(WaiterResourceDocumenter, self).__init__(resource)
self._service_waiter_model = service_waiter_model
def document_resource_waiters(self, section):
waiters = self._resource.meta.resource_model.waiters
add_resource_type_overview(
section=section,
resource_type='Waiters',
description=(
'Waiters provide an interface to wait for a resource'
' to reach a specific state.'),
intro_link='waiters_intro')
waiter_list = []
self.member_map['waiters'] = waiter_list
for waiter in waiters:
waiter_section = section.add_new_section(waiter.name)
waiter_list.append(waiter.name)
document_resource_waiter(
section=waiter_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
service_model=self._service_model,
resource_waiter_model=waiter,
service_waiter_model=self._service_waiter_model
)
def document_resource_waiter(section, resource_name, event_emitter,
service_model, resource_waiter_model,
service_waiter_model, include_signature=True):
waiter_model = service_waiter_model.get_waiter(
resource_waiter_model.waiter_name)
operation_model = service_model.operation_model(
waiter_model.operation)
ignore_params = get_resource_ignore_params(resource_waiter_model.params)
service_module_name = get_service_module_name(service_model)
description = (
'Waits until this %s is %s. This method calls '
':py:meth:`%s.Waiter.%s.wait` which polls. '
':py:meth:`%s.Client.%s` every %s seconds until '
'a successful state is reached. An error is returned '
'after %s failed checks.' % (
resource_name, ' '.join(resource_waiter_model.name.split('_')[2:]),
service_module_name,
xform_name(resource_waiter_model.waiter_name),
service_module_name,
xform_name(waiter_model.operation),
waiter_model.delay, waiter_model.max_attempts))
example_prefix = '%s.%s' % (
xform_name(resource_name), resource_waiter_model.name)
document_model_driven_method(
section=section, method_name=resource_waiter_model.name,
operation_model=operation_model,
event_emitter=event_emitter,
example_prefix=example_prefix,
method_description=description,
exclude_input=ignore_params,
include_signature=include_signature
)
if 'return' in section.available_sections:
# Waiters do not return anything so we should remove
# any sections that may document the underlying return
# value of the client method.
return_section = section.get_section('return')
return_section.clear_text()
return_section.remove_all_sections()
return_section.write(':returns: None')