Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/botocore/docs/bcdoc/restdoc.py: 34%
139 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1# Copyright 2012-2013 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 logging
14import os
15import re
17from botocore.compat import OrderedDict
18from botocore.docs.bcdoc.docstringparser import DocStringParser
19from botocore.docs.bcdoc.style import ReSTStyle
21DEFAULT_AWS_DOCS_LINK = 'https://docs.aws.amazon.com/index.html'
22DOCUMENTATION_LINK_REGEX = re.compile(
23 r'`AWS API Documentation '
24 r'<https://docs.aws.amazon.com/goto/WebAPI/[a-z0-9-.]*/[a-zA-Z]*>`_'
25)
26LARGE_SECTION_MESSAGE = """
28 **{}**
29 ::
31 # This section is too large to render.
32 # Please see the AWS API Documentation linked below.
34 {}
35 """
36LOG = logging.getLogger('bcdocs')
37SECTION_LINE_LIMIT_CONFIG = {
38 'response-example': {'name': 'Response Syntax', 'line_limit': 1500},
39 'description': {'name': 'Response Structure', 'line_limit': 5000},
40 'request-example': {'name': 'Request Syntax', 'line_limit': 1500},
41 'request-params': {'name': 'Parameters', 'line_limit': 5000},
42}
43SECTION_METHOD_PATH_DEPTH = {
44 'client-api': 4,
45 'paginator-api': 3,
46 'waiter-api': 3,
47}
50class ReSTDocument:
51 def __init__(self, target='man'):
52 self.style = ReSTStyle(self)
53 self.target = target
54 self.parser = DocStringParser(self)
55 self.keep_data = True
56 self.do_translation = False
57 self.translation_map = {}
58 self.hrefs = {}
59 self._writes = []
60 self._last_doc_string = None
62 def _write(self, s):
63 if self.keep_data and s is not None:
64 self._writes.append(s)
66 def write(self, content):
67 """
68 Write content into the document.
69 """
70 self._write(content)
72 def writeln(self, content):
73 """
74 Write content on a newline.
75 """
76 self._write(f'{self.style.spaces()}{content}\n')
78 def peek_write(self):
79 """
80 Returns the last content written to the document without
81 removing it from the stack.
82 """
83 return self._writes[-1]
85 def pop_write(self):
86 """
87 Removes and returns the last content written to the stack.
88 """
89 return self._writes.pop() if len(self._writes) > 0 else None
91 def push_write(self, s):
92 """
93 Places new content on the stack.
94 """
95 self._writes.append(s)
97 def getvalue(self):
98 """
99 Returns the current content of the document as a string.
100 """
101 if self.hrefs:
102 self.style.new_paragraph()
103 for refname, link in self.hrefs.items():
104 self.style.link_target_definition(refname, link)
105 return ''.join(self._writes).encode('utf-8')
107 def translate_words(self, words):
108 return [self.translation_map.get(w, w) for w in words]
110 def handle_data(self, data):
111 if data and self.keep_data:
112 self._write(data)
114 def include_doc_string(self, doc_string):
115 if doc_string:
116 try:
117 start = len(self._writes)
118 self.parser.feed(doc_string)
119 self.parser.close()
120 end = len(self._writes)
121 self._last_doc_string = (start, end)
122 except Exception:
123 LOG.debug('Error parsing doc string', exc_info=True)
124 LOG.debug(doc_string)
126 def remove_last_doc_string(self):
127 # Removes all writes inserted by last doc string
128 if self._last_doc_string is not None:
129 start, end = self._last_doc_string
130 del self._writes[start:end]
133class DocumentStructure(ReSTDocument):
134 def __init__(self, name, section_names=None, target='man', context=None):
135 """Provides a Hierarichial structure to a ReSTDocument
137 You can write to it similiar to as you can to a ReSTDocument but
138 has an innate structure for more orginaztion and abstraction.
140 :param name: The name of the document
141 :param section_names: A list of sections to be included
142 in the document.
143 :param target: The target documentation of the Document structure
144 :param context: A dictionary of data to store with the strucuture. These
145 are only stored per section not the entire structure.
146 """
147 super().__init__(target=target)
148 self._name = name
149 self._structure = OrderedDict()
150 self._path = [self._name]
151 self._context = {}
152 if context is not None:
153 self._context = context
154 if section_names is not None:
155 self._generate_structure(section_names)
157 @property
158 def name(self):
159 """The name of the document structure"""
160 return self._name
162 @property
163 def path(self):
164 """
165 A list of where to find a particular document structure in the
166 overlying document structure.
167 """
168 return self._path
170 @path.setter
171 def path(self, value):
172 self._path = value
174 @property
175 def available_sections(self):
176 return list(self._structure)
178 @property
179 def context(self):
180 return self._context
182 def _generate_structure(self, section_names):
183 for section_name in section_names:
184 self.add_new_section(section_name)
186 def add_new_section(self, name, context=None):
187 """Adds a new section to the current document structure
189 This document structure will be considered a section to the
190 current document structure but will in itself be an entirely
191 new document structure that can be written to and have sections
192 as well
194 :param name: The name of the section.
195 :param context: A dictionary of data to store with the strucuture. These
196 are only stored per section not the entire structure.
197 :rtype: DocumentStructure
198 :returns: A new document structure to add to but lives as a section
199 to the document structure it was instantiated from.
200 """
201 # Add a new section
202 section = self.__class__(
203 name=name, target=self.target, context=context
204 )
205 section.path = self.path + [name]
206 # Indent the section apporpriately as well
207 section.style.indentation = self.style.indentation
208 section.translation_map = self.translation_map
209 section.hrefs = self.hrefs
210 self._structure[name] = section
211 return section
213 def get_section(self, name):
214 """Retrieve a section"""
215 return self._structure[name]
217 def delete_section(self, name):
218 """Delete a section"""
219 del self._structure[name]
221 def flush_structure(self, docs_link=None):
222 """Flushes a doc structure to a ReSTructed string
224 The document is flushed out in a DFS style where sections and their
225 subsections' values are added to the string as they are visited.
226 """
227 # We are at the root flush the links at the beginning of the
228 # document
229 path_length = len(self.path)
230 if path_length == 1:
231 if self.hrefs:
232 self.style.new_paragraph()
233 for refname, link in self.hrefs.items():
234 self.style.link_target_definition(refname, link)
235 # Clear docs_link at the correct depth to prevent passing a non-related link.
236 elif path_length == SECTION_METHOD_PATH_DEPTH.get(self.path[1]):
237 docs_link = None
238 value = self.getvalue()
239 for name, section in self._structure.items():
240 # Checks is the AWS API Documentation link has been generated.
241 # If it has been generated, it gets passed as a the doc_link parameter.
242 match = DOCUMENTATION_LINK_REGEX.search(value.decode())
243 docs_link = (
244 f'{match.group(0)}\n\n'.encode() if match else docs_link
245 )
246 value += section.flush_structure(docs_link)
248 # Replace response/request sections if the line number exceeds our limit.
249 # The section is replaced with a message linking to AWS API Documentation.
250 line_count = len(value.splitlines())
251 section_config = SECTION_LINE_LIMIT_CONFIG.get(self.name)
252 aws_docs_link = (
253 docs_link.decode()
254 if docs_link is not None
255 else DEFAULT_AWS_DOCS_LINK
256 )
257 if section_config and line_count > section_config['line_limit']:
258 value = LARGE_SECTION_MESSAGE.format(
259 section_config['name'], aws_docs_link
260 ).encode()
261 return value
263 def getvalue(self):
264 return ''.join(self._writes).encode('utf-8')
266 def remove_all_sections(self):
267 self._structure = OrderedDict()
269 def clear_text(self):
270 self._writes = []
272 def add_title_section(self, title):
273 title_section = self.add_new_section('title')
274 title_section.style.h1(title)
275 return title_section
277 def write_to_file(self, full_path, file_name):
278 if not os.path.exists(full_path):
279 os.makedirs(full_path)
280 sub_resource_file_path = os.path.join(full_path, f'{file_name}.rst')
281 with open(sub_resource_file_path, 'wb') as f:
282 f.write(self.flush_structure())