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. 13import re 14import numbers 15from botocore.utils import parse_timestamp 16from botocore.docs.utils import escape_controls 17from botocore.compat import six 18 19 20class SharedExampleDocumenter(object): 21 def document_shared_example(self, example, prefix, section, 22 operation_model): 23 """Documents a single shared example based on its definition. 24 25 :param example: The model of the example 26 27 :param prefix: The prefix to use in the method example. 28 29 :param section: The section to write to. 30 31 :param operation_model: The model of the operation used in the example 32 """ 33 section.style.new_paragraph() 34 section.write(example.get('description')) 35 section.style.new_line() 36 self.document_input(section, example, prefix, 37 operation_model.input_shape) 38 self.document_output(section, example, operation_model.output_shape) 39 40 def document_input(self, section, example, prefix, shape): 41 input_section = section.add_new_section('input') 42 input_section.style.start_codeblock() 43 if prefix is not None: 44 input_section.write(prefix) 45 params = example.get('input', {}) 46 comments = example.get('comments') 47 if comments: 48 comments = comments.get('input') 49 param_section = input_section.add_new_section('parameters') 50 self._document_params(param_section, params, comments, [], shape) 51 closing_section = input_section.add_new_section('input-close') 52 closing_section.style.new_line() 53 closing_section.style.new_line() 54 closing_section.write('print(response)') 55 closing_section.style.end_codeblock() 56 57 def document_output(self, section, example, shape): 58 output_section = section.add_new_section('output') 59 output_section.style.new_line() 60 output_section.write('Expected Output:') 61 output_section.style.new_line() 62 output_section.style.start_codeblock() 63 params = example.get('output', {}) 64 65 # There might not be an output, but we will return metadata anyway 66 params['ResponseMetadata'] = {"...": "..."} 67 comments = example.get('comments') 68 if comments: 69 comments = comments.get('output') 70 self._document_dict(output_section, params, comments, [], shape, True) 71 closing_section = output_section.add_new_section('output-close') 72 closing_section.style.end_codeblock() 73 74 def _document(self, section, value, comments, path, shape): 75 """ 76 :param section: The section to add the docs to. 77 78 :param value: The input / output values representing the parameters that 79 are included in the example. 80 81 :param comments: The dictionary containing all the comments to be 82 applied to the example. 83 84 :param path: A list describing where the documenter is in traversing the 85 parameters. This is used to find the equivalent location 86 in the comments dictionary. 87 """ 88 if isinstance(value, dict): 89 self._document_dict(section, value, comments, path, shape) 90 elif isinstance(value, list): 91 self._document_list(section, value, comments, path, shape) 92 elif isinstance(value, numbers.Number): 93 self._document_number(section, value, path) 94 elif shape and shape.type_name == 'timestamp': 95 self._document_datetime(section, value, path) 96 else: 97 self._document_str(section, value, path) 98 99 def _document_dict(self, section, value, comments, path, shape, 100 top_level=False): 101 dict_section = section.add_new_section('dict-value') 102 self._start_nested_value(dict_section, '{') 103 for key, val in value.items(): 104 path.append('.%s' % key) 105 item_section = dict_section.add_new_section(key) 106 item_section.style.new_line() 107 item_comment = self._get_comment(path, comments) 108 if item_comment: 109 item_section.write(item_comment) 110 item_section.style.new_line() 111 item_section.write("'%s': " % key) 112 113 # Shape could be none if there is no output besides ResponseMetadata 114 item_shape = None 115 if shape: 116 if shape.type_name == 'structure': 117 item_shape = shape.members.get(key) 118 elif shape.type_name == 'map': 119 item_shape = shape.value 120 self._document(item_section, val, comments, path, item_shape) 121 path.pop() 122 dict_section_end = dict_section.add_new_section('ending-brace') 123 self._end_nested_value(dict_section_end, '}') 124 if not top_level: 125 dict_section_end.write(',') 126 127 def _document_params(self, section, value, comments, path, shape): 128 param_section = section.add_new_section('param-values') 129 self._start_nested_value(param_section, '(') 130 for key, val in value.items(): 131 path.append('.%s' % key) 132 item_section = param_section.add_new_section(key) 133 item_section.style.new_line() 134 item_comment = self._get_comment(path, comments) 135 if item_comment: 136 item_section.write(item_comment) 137 item_section.style.new_line() 138 item_section.write(key + '=') 139 140 # Shape could be none if there are no input parameters 141 item_shape = None 142 if shape: 143 item_shape = shape.members.get(key) 144 self._document(item_section, val, comments, path, item_shape) 145 path.pop() 146 param_section_end = param_section.add_new_section('ending-parenthesis') 147 self._end_nested_value(param_section_end, ')') 148 149 def _document_list(self, section, value, comments, path, shape): 150 list_section = section.add_new_section('list-section') 151 self._start_nested_value(list_section, '[') 152 item_shape = shape.member 153 for index, val in enumerate(value): 154 item_section = list_section.add_new_section(index) 155 item_section.style.new_line() 156 path.append('[%s]' % index) 157 item_comment = self._get_comment(path, comments) 158 if item_comment: 159 item_section.write(item_comment) 160 item_section.style.new_line() 161 self._document(item_section, val, comments, path, item_shape) 162 path.pop() 163 list_section_end = list_section.add_new_section('ending-bracket') 164 self._end_nested_value(list_section_end, '],') 165 166 def _document_str(self, section, value, path): 167 # We do the string conversion because this might accept a type that 168 # we don't specifically address. 169 safe_value = escape_controls(value) 170 section.write(u"'%s'," % six.text_type(safe_value)) 171 172 def _document_number(self, section, value, path): 173 section.write("%s," % str(value)) 174 175 def _document_datetime(self, section, value, path): 176 datetime_tuple = parse_timestamp(value).timetuple() 177 datetime_str = str(datetime_tuple[0]) 178 for i in range(1, len(datetime_tuple)): 179 datetime_str += ", " + str(datetime_tuple[i]) 180 section.write("datetime(%s)," % datetime_str) 181 182 def _get_comment(self, path, comments): 183 key = re.sub(r'^\.', '', ''.join(path)) 184 if comments and key in comments: 185 return '# ' + comments[key] 186 else: 187 return '' 188 189 def _start_nested_value(self, section, start): 190 section.write(start) 191 section.style.indent() 192 section.style.indent() 193 194 def _end_nested_value(self, section, end): 195 section.style.dedent() 196 section.style.dedent() 197 section.style.new_line() 198 section.write(end) 199 200 201def document_shared_examples(section, operation_model, example_prefix, 202 shared_examples): 203 """Documents the shared examples 204 205 :param section: The section to write to. 206 207 :param operation_model: The model of the operation. 208 209 :param example_prefix: The prefix to use in the method example. 210 211 :param shared_examples: The shared JSON examples from the model. 212 """ 213 container_section = section.add_new_section('shared-examples') 214 container_section.style.new_paragraph() 215 container_section.style.bold('Examples') 216 documenter = SharedExampleDocumenter() 217 for example in shared_examples: 218 documenter.document_shared_example( 219 example=example, 220 section=container_section.add_new_section(example['id']), 221 prefix=example_prefix, 222 operation_model=operation_model 223 ) 224