1# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"). You 4# may not use this file except in compliance with the License. A copy of 5# the License is located at 6# 7# http://aws.amazon.com/apache2.0/ 8# 9# or in the "license" file accompanying this file. This file is 10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11# ANY KIND, either express or implied. See the License for the specific 12# language governing permissions and limitations under the License. 13from tests import unittest 14from tests.unit.docs import BaseDocsTest 15from botocore.hooks import HierarchicalEmitter 16from botocore.docs.method import document_model_driven_signature 17from botocore.docs.method import document_custom_signature 18from botocore.docs.method import document_custom_method 19from botocore.docs.method import document_model_driven_method 20from botocore.docs.method import get_instance_public_methods 21from botocore.docs.utils import DocumentedShape 22 23 24class TestGetInstanceMethods(unittest.TestCase): 25 class MySampleClass(object): 26 def _internal_method(self): 27 pass 28 29 def public_method(self): 30 pass 31 32 def test_get_instance_methods(self): 33 instance = self.MySampleClass() 34 instance_methods = get_instance_public_methods(instance) 35 self.assertEqual(len(instance_methods), 1) 36 self.assertIn('public_method', instance_methods) 37 self.assertEqual( 38 instance.public_method, instance_methods['public_method']) 39 40 41class TestDocumentModelDrivenSignature(BaseDocsTest): 42 def setUp(self): 43 super(TestDocumentModelDrivenSignature, self).setUp() 44 self.add_shape_to_params('Foo', 'String') 45 self.add_shape_to_params('Bar', 'String', is_required=True) 46 self.add_shape_to_params('Baz', 'String') 47 48 def test_document_signature(self): 49 document_model_driven_signature( 50 self.doc_structure, 'my_method', self.operation_model) 51 self.assert_contains_line( 52 '.. py:method:: my_method(**kwargs)') 53 54 def test_document_signature_exclude_all_kwargs(self): 55 exclude_params = ['Foo', 'Bar', 'Baz'] 56 document_model_driven_signature( 57 self.doc_structure, 'my_method', self.operation_model, 58 exclude=exclude_params) 59 self.assert_contains_line( 60 '.. py:method:: my_method()') 61 62 def test_document_signature_exclude_and_include(self): 63 exclude_params = ['Foo', 'Bar', 'Baz'] 64 include_params = [ 65 DocumentedShape( 66 name='Biz', type_name='integer', documentation='biz docs') 67 ] 68 document_model_driven_signature( 69 self.doc_structure, 'my_method', self.operation_model, 70 include=include_params, exclude=exclude_params) 71 self.assert_contains_line( 72 '.. py:method:: my_method(**kwargs)') 73 74 75class TestDocumentCustomSignature(BaseDocsTest): 76 def sample_method(self, foo, bar='bar', baz=None): 77 pass 78 79 def test_document_signature(self): 80 document_custom_signature( 81 self.doc_structure, 'my_method', self.sample_method) 82 self.assert_contains_line( 83 '.. py:method:: my_method(foo, bar=\'bar\', baz=None)') 84 85 86class TestDocumentCustomMethod(BaseDocsTest): 87 def custom_method(self, foo): 88 """This is a custom method 89 90 :type foo: string 91 :param foo: The foo parameter 92 """ 93 pass 94 95 def test_document_custom_signature(self): 96 document_custom_method( 97 self.doc_structure, 'my_method', self.custom_method) 98 self.assert_contains_lines_in_order([ 99 '.. py:method:: my_method(foo)', 100 ' This is a custom method', 101 ' :type foo: string', 102 ' :param foo: The foo parameter' 103 ]) 104 105 106class TestDocumentModelDrivenMethod(BaseDocsTest): 107 def setUp(self): 108 super(TestDocumentModelDrivenMethod, self).setUp() 109 self.event_emitter = HierarchicalEmitter() 110 self.add_shape_to_params('Bar', 'String') 111 112 def test_default(self): 113 document_model_driven_method( 114 self.doc_structure, 'foo', self.operation_model, 115 event_emitter=self.event_emitter, 116 method_description='This describes the foo method.', 117 example_prefix='response = client.foo' 118 ) 119 cross_ref_link = ( 120 'See also: `AWS API Documentation ' 121 '<https://docs.aws.amazon.com/goto/WebAPI' 122 '/myservice-2014-01-01/SampleOperation>' 123 ) 124 self.assert_contains_lines_in_order([ 125 '.. py:method:: foo(**kwargs)', 126 ' This describes the foo method.', 127 cross_ref_link, 128 ' **Request Syntax**', 129 ' ::', 130 ' response = client.foo(', 131 ' Bar=\'string\'', 132 ' )', 133 ' :type Bar: string', 134 ' :param Bar:', 135 ' :rtype: dict', 136 ' :returns:', 137 ' **Response Syntax**', 138 ' ::', 139 ' {', 140 ' \'Bar\': \'string\'', 141 ' }', 142 ' **Response Structure**', 143 ' - *(dict) --*', 144 ' - **Bar** *(string) --*' 145 ]) 146 147 def test_no_input_output_shape(self): 148 del self.json_model['operations']['SampleOperation']['input'] 149 del self.json_model['operations']['SampleOperation']['output'] 150 document_model_driven_method( 151 self.doc_structure, 'foo', self.operation_model, 152 event_emitter=self.event_emitter, 153 method_description='This describes the foo method.', 154 example_prefix='response = client.foo' 155 ) 156 self.assert_contains_lines_in_order([ 157 '.. py:method:: foo()', 158 ' This describes the foo method.', 159 ' **Request Syntax**', 160 ' ::', 161 ' response = client.foo()', 162 ' :returns: None', 163 ]) 164 165 def test_include_input(self): 166 include_params = [ 167 DocumentedShape( 168 name='Biz', type_name='string', documentation='biz docs') 169 ] 170 document_model_driven_method( 171 self.doc_structure, 'foo', self.operation_model, 172 event_emitter=self.event_emitter, 173 method_description='This describes the foo method.', 174 example_prefix='response = client.foo', 175 include_input=include_params 176 ) 177 self.assert_contains_lines_in_order([ 178 '.. py:method:: foo(**kwargs)', 179 ' This describes the foo method.', 180 ' **Request Syntax**', 181 ' ::', 182 ' response = client.foo(', 183 ' Bar=\'string\',', 184 ' Biz=\'string\'', 185 ' )', 186 ' :type Bar: string', 187 ' :param Bar:', 188 ' :type Biz: string', 189 ' :param Biz: biz docs', 190 ' :rtype: dict', 191 ' :returns:', 192 ' **Response Syntax**', 193 ' ::', 194 ' {', 195 ' \'Bar\': \'string\'', 196 ' }', 197 ' **Response Structure**', 198 ' - *(dict) --*', 199 ' - **Bar** *(string) --*' 200 ]) 201 202 def test_include_output(self): 203 include_params = [ 204 DocumentedShape( 205 name='Biz', type_name='string', documentation='biz docs') 206 ] 207 document_model_driven_method( 208 self.doc_structure, 'foo', self.operation_model, 209 event_emitter=self.event_emitter, 210 method_description='This describes the foo method.', 211 example_prefix='response = client.foo', 212 include_output=include_params 213 ) 214 self.assert_contains_lines_in_order([ 215 '.. py:method:: foo(**kwargs)', 216 ' This describes the foo method.', 217 ' **Request Syntax**', 218 ' ::', 219 ' response = client.foo(', 220 ' Bar=\'string\'', 221 ' )', 222 ' :type Bar: string', 223 ' :param Bar:', 224 ' :rtype: dict', 225 ' :returns:', 226 ' **Response Syntax**', 227 ' ::', 228 ' {', 229 ' \'Bar\': \'string\'', 230 ' \'Biz\': \'string\'', 231 ' }', 232 ' **Response Structure**', 233 ' - *(dict) --*', 234 ' - **Bar** *(string) --*', 235 ' - **Biz** *(string) --*' 236 ]) 237 238 def test_exclude_input(self): 239 self.add_shape_to_params('Biz', 'String') 240 document_model_driven_method( 241 self.doc_structure, 'foo', self.operation_model, 242 event_emitter=self.event_emitter, 243 method_description='This describes the foo method.', 244 example_prefix='response = client.foo', 245 exclude_input=['Bar'] 246 ) 247 self.assert_contains_lines_in_order([ 248 '.. py:method:: foo(**kwargs)', 249 ' This describes the foo method.', 250 ' **Request Syntax**', 251 ' ::', 252 ' response = client.foo(', 253 ' Biz=\'string\'', 254 ' )', 255 ' :type Biz: string', 256 ' :param Biz:', 257 ' :rtype: dict', 258 ' :returns:', 259 ' **Response Syntax**', 260 ' ::', 261 ' {', 262 ' \'Bar\': \'string\'', 263 ' \'Biz\': \'string\'', 264 ' }', 265 ' **Response Structure**', 266 ' - *(dict) --*', 267 ' - **Bar** *(string) --*', 268 ' - **Biz** *(string) --*' 269 ]) 270 self.assert_not_contains_lines([ 271 ':param Bar: string', 272 'Bar=\'string\'' 273 ]) 274 275 def test_exclude_output(self): 276 self.add_shape_to_params('Biz', 'String') 277 document_model_driven_method( 278 self.doc_structure, 'foo', self.operation_model, 279 event_emitter=self.event_emitter, 280 method_description='This describes the foo method.', 281 example_prefix='response = client.foo', 282 exclude_output=['Bar'] 283 ) 284 self.assert_contains_lines_in_order([ 285 '.. py:method:: foo(**kwargs)', 286 ' This describes the foo method.', 287 ' **Request Syntax**', 288 ' ::', 289 ' response = client.foo(', 290 ' Bar=\'string\'', 291 ' Biz=\'string\'', 292 ' )', 293 ' :type Biz: string', 294 ' :param Biz:', 295 ' :rtype: dict', 296 ' :returns:', 297 ' **Response Syntax**', 298 ' ::', 299 ' {', 300 ' \'Biz\': \'string\'', 301 ' }', 302 ' **Response Structure**', 303 ' - *(dict) --*', 304 ' - **Biz** *(string) --*' 305 ]) 306 self.assert_not_contains_lines([ 307 '\'Bar\': \'string\'', 308 '- **Bar** *(string) --*', 309 ]) 310 311 def test_streaming_body_in_output(self): 312 self.add_shape_to_params('Body', 'Blob') 313 self.json_model['shapes']['Blob'] = {'type': 'blob'} 314 self.json_model['shapes']['SampleOperationInputOutput']['payload'] = \ 315 'Body' 316 document_model_driven_method( 317 self.doc_structure, 'foo', self.operation_model, 318 event_emitter=self.event_emitter, 319 method_description='This describes the foo method.', 320 example_prefix='response = client.foo' 321 ) 322 self.assert_contains_line('**Body** (:class:`.StreamingBody`)') 323 324 def test_event_stream_body_in_output(self): 325 self.add_shape_to_params('Payload', 'EventStream') 326 self.json_model['shapes']['SampleOperationInputOutput']['payload'] = \ 327 'Payload' 328 self.json_model['shapes']['EventStream'] = { 329 'type': 'structure', 330 'eventstream': True, 331 'members': {'Event': {'shape': 'Event'}} 332 } 333 self.json_model['shapes']['Event'] = { 334 'type': 'structure', 335 'event': True, 336 'members': { 337 'Fields': { 338 'shape': 'EventFields', 339 'eventpayload': True, 340 } 341 } 342 } 343 self.json_model['shapes']['EventFields'] = { 344 'type': 'structure', 345 'members': { 346 'Field': {'shape': 'EventField'} 347 } 348 } 349 self.json_model['shapes']['EventField'] = {'type': 'blob'} 350 document_model_driven_method( 351 self.doc_structure, 'foo', self.operation_model, 352 event_emitter=self.event_emitter, 353 method_description='This describes the foo method.', 354 example_prefix='response = client.foo' 355 ) 356 self.assert_contains_lines_in_order([ 357 "this operation contains an :class:`.EventStream`", 358 "'Payload': EventStream({", 359 "'Event': {", 360 "'Fields': {", 361 "'Field': b'bytes'", 362 "**Payload** (:class:`.EventStream`)", 363 "**Event** *(dict)", 364 "**Fields** *(dict)", 365 "**Field** *(bytes)", 366 ]) 367 368 def test_streaming_body_in_input(self): 369 del self.json_model['operations']['SampleOperation']['output'] 370 self.add_shape_to_params('Body', 'Blob') 371 self.json_model['shapes']['Blob'] = {'type': 'blob'} 372 self.json_model['shapes']['SampleOperationInputOutput']['payload'] = \ 373 'Body' 374 document_model_driven_method( 375 self.doc_structure, 'foo', self.operation_model, 376 event_emitter=self.event_emitter, 377 method_description='This describes the foo method.', 378 example_prefix='response = client.foo' 379 ) 380 # The line in the example 381 self.assert_contains_line('Body=b\'bytes\'|file') 382 # The line in the parameter description 383 self.assert_contains_line( 384 ':type Body: bytes or seekable file-like object') 385 386 def test_deprecated(self): 387 self.json_model['operations']['SampleOperation']['deprecated'] = True 388 document_model_driven_method( 389 self.doc_structure, 'foo', self.operation_model, 390 event_emitter=self.event_emitter, 391 method_description='This describes the foo method.', 392 example_prefix='response = client.foo' 393 ) 394 # The line in the example 395 self.assert_contains_lines_in_order([ 396 ' .. danger::', 397 ' This operation is deprecated and may not function as ' 398 'expected. This operation should not be used going forward and is ' 399 'only kept for the purpose of backwards compatiblity.' 400 ]) 401