codekingpro/portable-devtools
115k
1# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License"). You4# may not use this file except in compliance with the License. A copy of5# the License is located at6#7# http://aws.amazon.com/apache2.0/8#9# or in the "license" file accompanying this file. This file is10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF11# ANY KIND, either express or implied. See the License for the specific12# language governing permissions and limitations under the License.13import numbers14import re15 16from botocore.docs.utils import escape_controls17from botocore.utils import parse_timestamp18 19 20class SharedExampleDocumenter:21 def document_shared_example(22 self, example, prefix, section, operation_model23 ):24 """Documents a single shared example based on its definition.25 26 :param example: The model of the example27 28 :param prefix: The prefix to use in the method example.29 30 :param section: The section to write to.31 32 :param operation_model: The model of the operation used in the example33 """34 section.style.new_paragraph()35 section.write(example.get('description'))36 section.style.new_line()37 self.document_input(38 section, example, prefix, operation_model.input_shape39 )40 self.document_output(section, example, operation_model.output_shape)41 42 def document_input(self, section, example, prefix, shape):43 input_section = section.add_new_section('input')44 input_section.style.start_codeblock()45 if prefix is not None:46 input_section.write(prefix)47 params = example.get('input', {})48 comments = example.get('comments')49 if comments:50 comments = comments.get('input')51 param_section = input_section.add_new_section('parameters')52 self._document_params(param_section, params, comments, [], shape)53 closing_section = input_section.add_new_section('input-close')54 closing_section.style.new_line()55 closing_section.style.new_line()56 closing_section.write('print(response)')57 closing_section.style.end_codeblock()58 59 def document_output(self, section, example, shape):60 output_section = section.add_new_section('output')61 output_section.style.new_line()62 output_section.write('Expected Output:')63 output_section.style.new_line()64 output_section.style.start_codeblock()65 params = example.get('output', {})66 67 # There might not be an output, but we will return metadata anyway68 params['ResponseMetadata'] = {"...": "..."}69 comments = example.get('comments')70 if comments:71 comments = comments.get('output')72 self._document_dict(output_section, params, comments, [], shape, True)73 closing_section = output_section.add_new_section('output-close')74 closing_section.style.end_codeblock()75 76 def _document(self, section, value, comments, path, shape):77 """78 :param section: The section to add the docs to.79 80 :param value: The input / output values representing the parameters that81 are included in the example.82 83 :param comments: The dictionary containing all the comments to be84 applied to the example.85 86 :param path: A list describing where the documenter is in traversing the87 parameters. This is used to find the equivalent location88 in the comments dictionary.89 """90 if isinstance(value, dict):91 self._document_dict(section, value, comments, path, shape)92 elif isinstance(value, list):93 self._document_list(section, value, comments, path, shape)94 elif isinstance(value, numbers.Number):95 self._document_number(section, value, path)96 elif shape and shape.type_name == 'timestamp':97 self._document_datetime(section, value, path)98 else:99 self._document_str(section, value, path)100 101 def _document_dict(102 self, section, value, comments, path, shape, top_level=False103 ):104 dict_section = section.add_new_section('dict-value')105 self._start_nested_value(dict_section, '{')106 for key, val in value.items():107 path.append('.%s' % key)108 item_section = dict_section.add_new_section(key)109 item_section.style.new_line()110 item_comment = self._get_comment(path, comments)111 if item_comment:112 item_section.write(item_comment)113 item_section.style.new_line()114 item_section.write("'%s': " % key)115 116 # Shape could be none if there is no output besides ResponseMetadata117 item_shape = None118 if shape:119 if shape.type_name == 'structure':120 item_shape = shape.members.get(key)121 elif shape.type_name == 'map':122 item_shape = shape.value123 self._document(item_section, val, comments, path, item_shape)124 path.pop()125 dict_section_end = dict_section.add_new_section('ending-brace')126 self._end_nested_value(dict_section_end, '}')127 if not top_level:128 dict_section_end.write(',')129 130 def _document_params(self, section, value, comments, path, shape):131 param_section = section.add_new_section('param-values')132 self._start_nested_value(param_section, '(')133 for key, val in value.items():134 path.append('.%s' % key)135 item_section = param_section.add_new_section(key)136 item_section.style.new_line()137 item_comment = self._get_comment(path, comments)138 if item_comment:139 item_section.write(item_comment)140 item_section.style.new_line()141 item_section.write(key + '=')142 143 # Shape could be none if there are no input parameters144 item_shape = None145 if shape:146 item_shape = shape.members.get(key)147 self._document(item_section, val, comments, path, item_shape)148 path.pop()149 param_section_end = param_section.add_new_section('ending-parenthesis')150 self._end_nested_value(param_section_end, ')')151 152 def _document_list(self, section, value, comments, path, shape):153 list_section = section.add_new_section('list-section')154 self._start_nested_value(list_section, '[')155 item_shape = shape.member156 for index, val in enumerate(value):157 item_section = list_section.add_new_section(index)158 item_section.style.new_line()159 path.append('[%s]' % index)160 item_comment = self._get_comment(path, comments)161 if item_comment:162 item_section.write(item_comment)163 item_section.style.new_line()164 self._document(item_section, val, comments, path, item_shape)165 path.pop()166 list_section_end = list_section.add_new_section('ending-bracket')167 self._end_nested_value(list_section_end, '],')168 169 def _document_str(self, section, value, path):170 # We do the string conversion because this might accept a type that171 # we don't specifically address.172 safe_value = escape_controls(value)173 section.write(f"'{safe_value}',")174 175 def _document_number(self, section, value, path):176 section.write("%s," % str(value))177 178 def _document_datetime(self, section, value, path):179 datetime_tuple = parse_timestamp(value).timetuple()180 datetime_str = str(datetime_tuple[0])181 for i in range(1, len(datetime_tuple)):182 datetime_str += ", " + str(datetime_tuple[i])183 section.write("datetime(%s)," % datetime_str)184 185 def _get_comment(self, path, comments):186 key = re.sub(r'^\.', '', ''.join(path))187 if comments and key in comments:188 return '# ' + comments[key]189 else:190 return ''191 192 def _start_nested_value(self, section, start):193 section.write(start)194 section.style.indent()195 section.style.indent()196 197 def _end_nested_value(self, section, end):198 section.style.dedent()199 section.style.dedent()200 section.style.new_line()201 section.write(end)202 203 204def document_shared_examples(205 section, operation_model, example_prefix, shared_examples206):207 """Documents the shared examples208 209 :param section: The section to write to.210 211 :param operation_model: The model of the operation.212 213 :param example_prefix: The prefix to use in the method example.214 215 :param shared_examples: The shared JSON examples from the model.216 """217 container_section = section.add_new_section('shared-examples')218 container_section.style.new_paragraph()219 container_section.style.bold('Examples')220 documenter = SharedExampleDocumenter()221 for example in shared_examples:222 documenter.document_shared_example(223 example=example,224 section=container_section.add_new_section(example['id']),225 prefix=example_prefix,226 operation_model=operation_model,227 )228 