Skip to content

Commit 9677fd4

Browse files
authored
Merge pull request #7205 from hssyoo/doc-tagged-unions
Add note for Tagged Unions
2 parents d95a1a8 + 15472bf commit 9677fd4

File tree

5 files changed

+94
-4
lines changed

5 files changed

+94
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "enhancement",
3+
"category": "docs",
4+
"description": "Generate a usage note for Tagged Union structures."
5+
}

awscli/clidocs.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
from awscli.topictags import TopicTagDB
2424
from awscli.utils import (
2525
find_service_and_method_in_event_name, is_document_type,
26-
operation_uses_document_types, is_streaming_blob_type
26+
operation_uses_document_types, is_streaming_blob_type,
27+
is_tagged_union_type
2728
)
2829

2930
LOG = logging.getLogger(__name__)
@@ -56,6 +57,8 @@ def _get_argument_type_name(self, shape, default):
5657
return 'document'
5758
if is_streaming_blob_type(shape):
5859
return 'streaming blob'
60+
if is_tagged_union_type(shape):
61+
return 'tagged union structure'
5962
return default
6063

6164
def _map_handlers(self, session, event_class, mapfn):
@@ -185,6 +188,8 @@ def doc_option(self, arg_name, help_command, **kwargs):
185188
doc.include_doc_string(argument.documentation)
186189
if is_streaming_blob_type(argument.argument_model):
187190
self._add_streaming_blob_note(doc)
191+
if is_tagged_union_type(argument.argument_model):
192+
self._add_tagged_union_note(argument.argument_model, doc)
188193
if hasattr(argument, 'argument_model'):
189194
self._document_enums(argument.argument_model, doc)
190195
self._document_nested_structure(argument.argument_model, doc)
@@ -264,6 +269,8 @@ def _do_doc_member(self, doc, member_name, member_shape, stack):
264269
doc.style.indent()
265270
doc.style.new_paragraph()
266271
doc.include_doc_string(docs)
272+
if is_tagged_union_type(member_shape):
273+
self._add_tagged_union_note(member_shape, doc)
267274
doc.style.new_paragraph()
268275
member_type_name = member_shape.type_name
269276
if member_type_name == 'structure':
@@ -290,6 +297,16 @@ def _add_streaming_blob_note(self, doc):
290297
doc.writeln(msg)
291298
doc.style.end_note()
292299

300+
def _add_tagged_union_note(self, shape, doc):
301+
doc.style.start_note()
302+
members_str = ", ".join(
303+
[f'``{key}``' for key in shape.members.keys()]
304+
)
305+
msg = ("This is a Tagged Union structure. Only one of the "
306+
f"following top level keys can be set: {members_str}.")
307+
doc.writeln(msg)
308+
doc.style.end_note()
309+
293310

294311
class ProviderDocumentEventHandler(CLIDocumentEventHandler):
295312

awscli/utils.py

+5
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ def is_streaming_blob_type(shape):
159159
shape.serialization.get('streaming', False))
160160

161161

162+
def is_tagged_union_type(shape):
163+
"""Check if the shape is a tagged union structure."""
164+
return getattr(shape, 'is_tagged_union', False)
165+
166+
162167
def operation_uses_document_types(operation_model):
163168
"""Check if document types are ever used in the operation"""
164169
recording_visitor = ShapeRecordingVisitor()

tests/unit/test_clidocs.py

+54-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import json
1414

1515
from botocore.model import ShapeResolver, StructureShape, StringShape, \
16-
ListShape, MapShape, Shape
16+
ListShape, MapShape, Shape, DenormalizedStructureBuilder
1717

1818
from awscli.testutils import mock, unittest, FileCreator
1919
from awscli.clidocs import OperationDocumentEventHandler, \
@@ -150,6 +150,15 @@ def create_help_command(self):
150150
help_command.obj = operation_model
151151
return help_command
152152

153+
def create_tagged_union_shape(self):
154+
shape_model = {
155+
'type': 'structure',
156+
'union': True,
157+
'members': {}
158+
}
159+
tagged_union = StructureShape('tagged_union', shape_model)
160+
return tagged_union
161+
153162
def get_help_docs_for_argument(self, shape):
154163
arg_table = {'arg-name': mock.Mock(argument_model=shape)}
155164
help_command = mock.Mock()
@@ -391,6 +400,50 @@ def test_streaming_blob_comes_after_docstring(self):
391400
rendered = help_command.doc.getvalue().decode('utf-8')
392401
self.assertRegex(rendered, r'FooBar[\s\S]*streaming blob')
393402

403+
def test_includes_tagged_union_options(self):
404+
help_command = self.create_help_command()
405+
tagged_union = self.create_tagged_union_shape()
406+
arg = CustomArgument(name='tagged_union',
407+
argument_model=tagged_union)
408+
help_command.arg_table = {'tagged_union': arg}
409+
operation_handler = OperationDocumentEventHandler(help_command)
410+
operation_handler.doc_option(arg_name='tagged_union',
411+
help_command=help_command)
412+
rendered = help_command.doc.getvalue().decode('utf-8')
413+
self.assertIn('(tagged union structure)', rendered)
414+
415+
def test_tagged_union_comes_after_docstring_options(self):
416+
help_command = self.create_help_command()
417+
tagged_union = self.create_tagged_union_shape()
418+
arg = CustomArgument(name='tagged_union',
419+
argument_model=tagged_union,
420+
help_text='FooBar')
421+
help_command.arg_table = {'tagged_union': arg}
422+
operation_handler = OperationDocumentEventHandler(help_command)
423+
operation_handler.doc_option(arg_name='tagged_union',
424+
help_command=help_command)
425+
rendered = help_command.doc.getvalue().decode('utf-8')
426+
self.assertRegex(rendered, r'FooBar[\s\S]*Tagged Union')
427+
428+
def test_tagged_union_comes_after_docstring_output(self):
429+
help_command = self.create_help_command()
430+
tagged_union = self.create_tagged_union_shape()
431+
tagged_union.documentation = "FooBar"
432+
shape = DenormalizedStructureBuilder().with_members({
433+
'foo': {
434+
'type': 'structure',
435+
'union': True,
436+
'documentation': 'FooBar',
437+
'members': {}
438+
}
439+
}).build_model()
440+
help_command.obj.output_shape = shape
441+
operation_handler = OperationDocumentEventHandler(help_command)
442+
operation_handler.doc_output(help_command=help_command,
443+
event_name='foobar')
444+
rendered = help_command.doc.getvalue().decode('utf-8')
445+
self.assertRegex(rendered, r'FooBar[\s\S]*Tagged Union')
446+
394447

395448
class TestTopicDocumentEventHandlerBase(unittest.TestCase):
396449
def setUp(self):

tests/unit/test_utils.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
from awscli.utils import (
2323
split_on_commas, ignore_ctrl_c, find_service_and_method_in_event_name,
2424
is_document_type, is_document_type_container, is_streaming_blob_type,
25-
operation_uses_document_types, ShapeWalker, ShapeRecordingVisitor,
26-
OutputStreamFactory
25+
is_tagged_union_type, operation_uses_document_types, ShapeWalker,
26+
ShapeRecordingVisitor, OutputStreamFactory
2727
)
2828

2929

@@ -433,3 +433,13 @@ def test_non_blob_is_not_streaming(self, argument_model):
433433
argument_model.type_name = 'string'
434434
argument_model.serialization = {}
435435
assert not is_streaming_blob_type(argument_model)
436+
437+
438+
@pytest.mark.usefixtures('argument_model')
439+
class TestTaggedUnion:
440+
def test_shape_is_tagged_union(self, argument_model):
441+
setattr(argument_model, 'is_tagged_union', True)
442+
assert is_tagged_union_type(argument_model)
443+
444+
def test_shape_is_not_tagged_union(self, argument_model):
445+
assert not is_tagged_union_type(argument_model)

0 commit comments

Comments
 (0)