Skip to content

Commit 66b1552

Browse files
committed
Add docs to coverage, review updates.
This adds a slew of new tests and modifies the code to use helper methods.
1 parent 9cea13b commit 66b1552

File tree

6 files changed

+552
-67
lines changed

6 files changed

+552
-67
lines changed

.coveragerc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,3 @@
22
branch = True
33
include =
44
boto3/*
5-
omit =
6-
boto3/docs.py

boto3/docs.py

Lines changed: 114 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,20 @@ def html_to_rst(html, indent=0, indent_first=False):
120120
return rst
121121

122122

123-
def docs_for(service_name):
123+
def docs_for(service_name, session=None, resource_filename=None):
124124
"""
125125
Generate reference documentation (low and high level) for a service
126126
by name. This generates docs for the latest available version.
127127
128128
:type service_name: string
129129
:param service_name: The service short-name, like 'ec2'
130+
:type session: botocore.session.Session
131+
:param session: Existing pre-setup session or ``None``.
130132
:rtype: string
131133
"""
132-
session = botocore.session.get_session()
134+
if session is None:
135+
session = botocore.session.get_session()
136+
133137
service_model = session.get_service_model(service_name)
134138

135139
print('Processing {0}-{1}'.format(service_name, service_model.api_version))
@@ -151,17 +155,26 @@ def docs_for(service_name):
151155

152156
docs += '.. contents:: Table of Contents\n :depth: 2\n\n'
153157

154-
docs += document_client(service_name, official_name, service_model)
158+
# TODO: Get this information from the model somehow in the future.
159+
# For now creating and introspecing a client is a quick and
160+
# dirty way to access waiters/paginators.
161+
client = session.create_client(service_name, aws_access_key_id='dummy',
162+
aws_secret_access_key='dummy',
163+
region_name='us-east-1')
164+
165+
docs += document_client(service_name, official_name, service_model,
166+
client)
155167
docs += document_client_waiter(session, official_name, service_name,
156-
service_model)
168+
service_model, client)
157169

158-
filename = (os.path.dirname(__file__) + '/data/resources/'
159-
'{0}-{1}.resources.json').format(service_name,
160-
service_model.api_version)
170+
if resource_filename is None:
171+
resource_filename = (os.path.dirname(__file__) + '/data/resources/'
172+
'{0}-{1}.resources.json').format(
173+
service_name, service_model.api_version)
161174
# We can't use a set here because dicts aren't hashable!
162175
models = {}
163-
if os.path.exists(filename):
164-
data = json.load(open(filename))
176+
if os.path.exists(resource_filename):
177+
data = json.load(open(resource_filename))
165178
model = ResourceModel(service_name, data['service'], data['resources'])
166179

167180
for collection_model in model.collections:
@@ -208,7 +221,7 @@ def docs_for(service_name):
208221

209222
return docs
210223

211-
def document_client(service_name, official_name, service_model):
224+
def document_client(service_name, official_name, service_model, client):
212225
"""
213226
Generate low-level client documentation for a service. This generates
214227
documentation for all available operations.
@@ -221,13 +234,6 @@ def document_client(service_name, official_name, service_model):
221234
docs += ' {service} = boto3.client(\'{service}\')\n\n'.format(
222235
service=service_name)
223236

224-
# TODO: Get this information from the model somehow in the future.
225-
# For now creating and introspecing a client is a quick and
226-
# dirty way to access waiters/paginators.
227-
client = boto3.client(service_name, aws_access_key_id='dummy',
228-
aws_secret_access_key='dummy',
229-
region_name='us-east-1')
230-
231237
wdoc = ''
232238
if client.waiter_names:
233239
# This gets included in alphabetical order below!
@@ -252,10 +258,7 @@ def document_client(service_name, official_name, service_model):
252258
return docs
253259

254260
def document_client_waiter(session, official_name, service_name,
255-
service_model):
256-
client = boto3.client(service_name, aws_access_key_id='dummy',
257-
aws_secret_access_key='dummy',
258-
region_name='us-east-1')
261+
service_model, client):
259262
waiter_spec_doc = ''
260263
if client.waiter_names:
261264
waiter_spec_doc = 'Waiter\n------\n\n'
@@ -556,7 +559,7 @@ def document_operation(operation_model, service_name, operation_name=None,
556559
param_desc = ', '.join([
557560
', '.join(['{0}=None'.format(k) for k in required_params]),
558561
', '.join(['{0}=None'.format(k) for k in optional_params])
559-
])
562+
]).strip(', ')
560563

561564
if operation_name is None:
562565
operation_name = xform_name(operation_model.name)
@@ -601,7 +604,7 @@ def document_operation(operation_model, service_name, operation_name=None,
601604
if param_type in ['list', 'dict']:
602605
param_desc = ('\n Structure description::\n\n' +
603606
' ' + key + ' = ' +
604-
document_structure(
607+
document_param_response(
605608
key, value, indent=12, indent_first=False) +
606609
'\n' + param_desc)
607610
required = key in required_params and 'Required' or 'Optional'
@@ -615,21 +618,13 @@ def document_operation(operation_model, service_name, operation_name=None,
615618
output_shape = operation_model.output_shape
616619
if rtype in ['list', 'dict'] and output_shape is not None:
617620
docs += (' :return:\n Structure description::\n\n' +
618-
document_structure(None, output_shape, indent=12) + '\n')
621+
document_param_response(None, output_shape, indent=12) + '\n')
619622

620623
return docs
621624

622625

623-
PARAM_NAME = "'{name}': "
624-
ELLIPSIS = '...\n'
625-
STRUCT_START = '{\n'
626-
STRUCT_END = '}'
627-
LIST_START = '[\n'
628-
LIST_END = ']'
629-
630-
631-
def document_structure(name, shape, indent=0, indent_first=True,
632-
parent_type=None, eol='\n'):
626+
def document_param_response(name, shape, indent=0, indent_first=True,
627+
parent_type=None, eol='\n'):
633628
"""
634629
Document a nested structure (list or dict) parameter or return value as
635630
a snippet of Python code with dummy placeholders. For example:
@@ -661,6 +656,7 @@ def document_structure(name, shape, indent=0, indent_first=True,
661656
:param eol: The end-of-line string to use when finishing a member.
662657
:rtype: string
663658
"""
659+
param_name = "'{name}': "
664660
docs = ''
665661
spaces = ' ' * indent
666662

@@ -671,43 +667,100 @@ def document_structure(name, shape, indent=0, indent_first=True,
671667
if shape.type_name == 'structure':
672668
# Only include the name if the parent is also a structure.
673669
if parent_type == 'structure':
674-
docs += PARAM_NAME.format(name=name)
675-
676-
docs += STRUCT_START
677-
678-
# Go through each member and recursively process them.
679-
for i, member_name in enumerate(shape.members):
680-
# Individual members get a trailing comma only if they
681-
# are not the last item.
682-
member_eol = '\n'
683-
if i < len(shape.members) - 1:
684-
member_eol = ',' + member_eol
685-
686-
docs += document_structure(
687-
member_name, shape.members[member_name],
688-
indent=indent + 4, parent_type=shape.type_name,
689-
eol=member_eol)
690-
docs += spaces + STRUCT_END + eol
670+
docs += param_name.format(name=name)
671+
672+
docs += render_structure(shape, spaces, indent, eol)
691673
elif shape.type_name == 'list':
692674
# Only include the name if the parent is a structure.
693675
if parent_type == 'structure':
694-
docs += PARAM_NAME.format(name=name)
676+
docs += param_name.format(name=name)
695677

696-
docs += LIST_START
678+
docs += render_list(shape, spaces, indent, eol)
679+
elif shape.type_name == 'map':
680+
if parent_type == 'structure':
681+
docs += param_name.format(name=name)
697682

698-
# Lists have only a single member. Here we document it, plus add
699-
# an ellipsis to signify that more of the same member type can be
700-
# added in a list.
701-
docs += document_structure(
702-
None, shape.member, indent=indent + 4, eol=',\n')
703-
docs += spaces + ' ' + ELLIPSIS
704-
docs += spaces + LIST_END + eol
683+
docs += render_map(shape, spaces, indent, eol)
705684
else:
706685
# It's not a structure or list, so document the type. Here we
707686
# try to use the equivalent Python type name for clarity.
708687
if name is not None:
709-
docs += PARAM_NAME.format(name=name)
688+
docs += param_name.format(name=name)
710689

711690
docs += py_type_name(shape.type_name).upper() + eol
712691

713692
return docs
693+
694+
695+
def render_structure(shape, spaces, indent, eol):
696+
"""
697+
Render out a ``structure`` type. This renders info about each
698+
member in the shape::
699+
700+
{
701+
'MemberName': 'Value'
702+
}
703+
704+
"""
705+
docs = '{\n'
706+
707+
# Go through each member and recursively process them.
708+
for i, member_name in enumerate(shape.members):
709+
# Individual members get a trailing comma only if they
710+
# are not the last item.
711+
member_eol = '\n'
712+
if i < len(shape.members) - 1:
713+
member_eol = ',' + member_eol
714+
715+
docs += document_param_response(
716+
member_name, shape.members[member_name],
717+
indent=indent + 4, parent_type=shape.type_name,
718+
eol=member_eol)
719+
docs += spaces + '}' + eol
720+
721+
return docs
722+
723+
724+
def render_list(shape, spaces, indent, eol):
725+
"""
726+
Render out a ``list`` type. This renders info about the member
727+
type of the list and adds an ellipsis to indicate it can contain
728+
many items::
729+
730+
[
731+
'STRING',
732+
...
733+
]
734+
735+
"""
736+
docs = '[\n'
737+
738+
# Lists have only a single member. Here we document it, plus add
739+
# an ellipsis to signify that more of the same member type can be
740+
# added in a list.
741+
docs += document_param_response(
742+
None, shape.member, indent=indent + 4, eol=',\n')
743+
docs += spaces + ' ...\n'
744+
docs += spaces + ']' + eol
745+
746+
return docs
747+
748+
749+
def render_map(shape, spaces, indent, eol):
750+
"""
751+
Render out a ``structure`` type. This renders info about each
752+
member in the shape::
753+
754+
{
755+
'MemberName': 'Value'
756+
}
757+
758+
"""
759+
docs = '{\n'
760+
761+
# Document the types for the keys and values.
762+
docs += (spaces + ' ' + py_type_name(shape.key.type_name).upper()
763+
+ ': ' + py_type_name(shape.value.type_name).upper() + '\n')
764+
docs += spaces + '}' + eol
765+
766+
return docs

0 commit comments

Comments
 (0)