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# https://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 os
14
15from botocore import xform_name
16from botocore.docs.bcdoc.restdoc import DocumentStructure
17from botocore.docs.utils import get_official_service_name
18
19from boto3.docs.action import ActionDocumenter
20from boto3.docs.attr import (
21 document_attribute,
22 document_identifier,
23 document_reference,
24)
25from boto3.docs.base import BaseDocumenter
26from boto3.docs.collection import CollectionDocumenter
27from boto3.docs.subresource import SubResourceDocumenter
28from boto3.docs.utils import (
29 add_resource_type_overview,
30 get_identifier_args_for_signature,
31 get_identifier_description,
32 get_identifier_values_for_example,
33)
34from boto3.docs.waiter import WaiterResourceDocumenter
35
36
37class ResourceDocumenter(BaseDocumenter):
38 def __init__(self, resource, botocore_session, root_docs_path):
39 super().__init__(resource)
40 self._botocore_session = botocore_session
41 self._root_docs_path = root_docs_path
42 self._resource_sub_path = self._resource_name.lower()
43 if self._resource_name == self._service_name:
44 self._resource_sub_path = 'service-resource'
45
46 def document_resource(self, section):
47 self._add_title(section)
48 self._add_resource_note(section)
49 self._add_intro(section)
50 self._add_identifiers(section)
51 self._add_attributes(section)
52 self._add_references(section)
53 self._add_actions(section)
54 self._add_sub_resources(section)
55 self._add_collections(section)
56 self._add_waiters(section)
57
58 def _add_title(self, section):
59 title_section = section.add_new_section('title')
60 title_section.style.h2(self._resource_name)
61
62 def _add_intro(self, section):
63 identifier_names = []
64 if self._resource_model.identifiers:
65 for identifier in self._resource_model.identifiers:
66 identifier_names.append(identifier.name)
67
68 # Write out the class signature.
69 class_args = get_identifier_args_for_signature(identifier_names)
70 start_class = section.add_new_section('start_class')
71 start_class.style.start_sphinx_py_class(
72 class_name=f'{self.class_name}({class_args})'
73 )
74
75 # Add as short description about the resource
76 description_section = start_class.add_new_section('description')
77 self._add_description(description_section)
78
79 # Add an example of how to instantiate the resource
80 example_section = start_class.add_new_section('example')
81 self._add_example(example_section, identifier_names)
82
83 # Add the description for the parameters to instantiate the
84 # resource.
85 param_section = start_class.add_new_section('params')
86 self._add_params_description(param_section, identifier_names)
87
88 end_class = section.add_new_section('end_class')
89 end_class.style.end_sphinx_py_class()
90
91 def _add_description(self, section):
92 official_service_name = get_official_service_name(self._service_model)
93 section.write(
94 f'A resource representing an {official_service_name} {self._resource_name}'
95 )
96
97 def _add_example(self, section, identifier_names):
98 section.style.start_codeblock()
99 section.style.new_line()
100 section.write('import boto3')
101 section.style.new_line()
102 section.style.new_line()
103 section.write(
104 f'{self._service_name} = boto3.resource(\'{self._service_name}\')'
105 )
106 section.style.new_line()
107 example_values = get_identifier_values_for_example(identifier_names)
108 section.write(
109 f'{xform_name(self._resource_name)} = {self._service_name}.{self._resource_name}({example_values})'
110 )
111 section.style.end_codeblock()
112
113 def _add_params_description(self, section, identifier_names):
114 for identifier_name in identifier_names:
115 description = get_identifier_description(
116 self._resource_name, identifier_name
117 )
118 section.write(f':type {identifier_name}: string')
119 section.style.new_line()
120 section.write(f':param {identifier_name}: {description}')
121 section.style.new_line()
122
123 def _add_overview_of_member_type(self, section, resource_member_type):
124 section.style.new_line()
125 section.write(
126 f'These are the resource\'s available {resource_member_type}:'
127 )
128 section.style.new_line()
129 section.style.toctree()
130 for member in self.member_map[resource_member_type]:
131 section.style.tocitem(f'{member}')
132
133 def _add_identifiers(self, section):
134 identifiers = self._resource.meta.resource_model.identifiers
135 section = section.add_new_section('identifiers')
136 member_list = []
137 if identifiers:
138 self.member_map['identifiers'] = member_list
139 add_resource_type_overview(
140 section=section,
141 resource_type='Identifiers',
142 description=(
143 'Identifiers are properties of a resource that are '
144 'set upon instantiation of the resource.'
145 ),
146 intro_link='identifiers_attributes_intro',
147 )
148 for identifier in identifiers:
149 member_list.append(identifier.name)
150 # Create a new DocumentStructure for each identifier and add contents.
151 identifier_doc = DocumentStructure(identifier.name, target='html')
152 breadcrumb_section = identifier_doc.add_new_section('breadcrumb')
153 breadcrumb_section.style.ref(self._resource_class_name, 'index')
154 breadcrumb_section.write(f' / Identifier / {identifier.name}')
155 identifier_doc.add_title_section(identifier.name)
156 identifier_section = identifier_doc.add_new_section(
157 identifier.name,
158 context={'qualifier': f'{self.class_name}.'},
159 )
160 document_identifier(
161 section=identifier_section,
162 resource_name=self._resource_name,
163 identifier_model=identifier,
164 )
165 # Write identifiers in individual/nested files.
166 # Path: <root>/reference/services/<service>/<resource_name>/<identifier_name>.rst
167 identifiers_dir_path = os.path.join(
168 self._root_docs_path,
169 f'{self._service_name}',
170 f'{self._resource_sub_path}',
171 )
172 identifier_doc.write_to_file(identifiers_dir_path, identifier.name)
173
174 if identifiers:
175 self._add_overview_of_member_type(section, 'identifiers')
176
177 def _add_attributes(self, section):
178 service_model = self._resource.meta.client.meta.service_model
179 attributes = {}
180 if self._resource.meta.resource_model.shape:
181 shape = service_model.shape_for(
182 self._resource.meta.resource_model.shape
183 )
184 attributes = self._resource.meta.resource_model.get_attributes(
185 shape
186 )
187 section = section.add_new_section('attributes')
188 attribute_list = []
189 if attributes:
190 add_resource_type_overview(
191 section=section,
192 resource_type='Attributes',
193 description=(
194 'Attributes provide access'
195 ' to the properties of a resource. Attributes are lazy-'
196 'loaded the first time one is accessed via the'
197 ' :py:meth:`load` method.'
198 ),
199 intro_link='identifiers_attributes_intro',
200 )
201 self.member_map['attributes'] = attribute_list
202 for attr_name in sorted(attributes):
203 _, attr_shape = attributes[attr_name]
204 attribute_list.append(attr_name)
205 # Create a new DocumentStructure for each attribute and add contents.
206 attribute_doc = DocumentStructure(attr_name, target='html')
207 breadcrumb_section = attribute_doc.add_new_section('breadcrumb')
208 breadcrumb_section.style.ref(self._resource_class_name, 'index')
209 breadcrumb_section.write(f' / Attribute / {attr_name}')
210 attribute_doc.add_title_section(attr_name)
211 attribute_section = attribute_doc.add_new_section(
212 attr_name,
213 context={'qualifier': f'{self.class_name}.'},
214 )
215 document_attribute(
216 section=attribute_section,
217 service_name=self._service_name,
218 resource_name=self._resource_name,
219 attr_name=attr_name,
220 event_emitter=self._resource.meta.client.meta.events,
221 attr_model=attr_shape,
222 )
223 # Write attributes in individual/nested files.
224 # Path: <root>/reference/services/<service>/<resource_name>/<attribute_name>.rst
225 attributes_dir_path = os.path.join(
226 self._root_docs_path,
227 f'{self._service_name}',
228 f'{self._resource_sub_path}',
229 )
230 attribute_doc.write_to_file(attributes_dir_path, attr_name)
231 if attributes:
232 self._add_overview_of_member_type(section, 'attributes')
233
234 def _add_references(self, section):
235 section = section.add_new_section('references')
236 references = self._resource.meta.resource_model.references
237 reference_list = []
238 if references:
239 add_resource_type_overview(
240 section=section,
241 resource_type='References',
242 description=(
243 'References are related resource instances that have '
244 'a belongs-to relationship.'
245 ),
246 intro_link='references_intro',
247 )
248 self.member_map['references'] = reference_list
249 for reference in references:
250 reference_list.append(reference.name)
251 # Create a new DocumentStructure for each reference and add contents.
252 reference_doc = DocumentStructure(reference.name, target='html')
253 breadcrumb_section = reference_doc.add_new_section('breadcrumb')
254 breadcrumb_section.style.ref(self._resource_class_name, 'index')
255 breadcrumb_section.write(f' / Reference / {reference.name}')
256 reference_doc.add_title_section(reference.name)
257 reference_section = reference_doc.add_new_section(
258 reference.name,
259 context={'qualifier': f'{self.class_name}.'},
260 )
261 document_reference(
262 section=reference_section,
263 reference_model=reference,
264 )
265 # Write references in individual/nested files.
266 # Path: <root>/reference/services/<service>/<resource_name>/<reference_name>.rst
267 references_dir_path = os.path.join(
268 self._root_docs_path,
269 f'{self._service_name}',
270 f'{self._resource_sub_path}',
271 )
272 reference_doc.write_to_file(references_dir_path, reference.name)
273 if references:
274 self._add_overview_of_member_type(section, 'references')
275
276 def _add_actions(self, section):
277 section = section.add_new_section('actions')
278 actions = self._resource.meta.resource_model.actions
279 if actions:
280 documenter = ActionDocumenter(self._resource, self._root_docs_path)
281 documenter.member_map = self.member_map
282 documenter.document_actions(section)
283 self._add_overview_of_member_type(section, 'actions')
284
285 def _add_sub_resources(self, section):
286 section = section.add_new_section('sub-resources')
287 sub_resources = self._resource.meta.resource_model.subresources
288 if sub_resources:
289 documenter = SubResourceDocumenter(
290 self._resource, self._root_docs_path
291 )
292 documenter.member_map = self.member_map
293 documenter.document_sub_resources(section)
294 self._add_overview_of_member_type(section, 'sub-resources')
295
296 def _add_collections(self, section):
297 section = section.add_new_section('collections')
298 collections = self._resource.meta.resource_model.collections
299 if collections:
300 documenter = CollectionDocumenter(
301 self._resource, self._root_docs_path
302 )
303 documenter.member_map = self.member_map
304 documenter.document_collections(section)
305 self._add_overview_of_member_type(section, 'collections')
306
307 def _add_waiters(self, section):
308 section = section.add_new_section('waiters')
309 waiters = self._resource.meta.resource_model.waiters
310 if waiters:
311 service_waiter_model = self._botocore_session.get_waiter_model(
312 self._service_name
313 )
314 documenter = WaiterResourceDocumenter(
315 self._resource, service_waiter_model, self._root_docs_path
316 )
317 documenter.member_map = self.member_map
318 documenter.document_resource_waiters(section)
319 self._add_overview_of_member_type(section, 'waiters')
320
321 def _add_resource_note(self, section):
322 section = section.add_new_section('feature-freeze')
323 section.style.start_note()
324 section.write(
325 "Before using anything on this page, please refer to the resources "
326 ":doc:`user guide <../../../../guide/resources>` for the most recent "
327 "guidance on using resources."
328 )
329 section.style.end_note()
330
331
332class ServiceResourceDocumenter(ResourceDocumenter):
333 @property
334 def class_name(self):
335 return f'{self._service_docs_name}.ServiceResource'
336
337 def _add_title(self, section):
338 title_section = section.add_new_section('title')
339 title_section.style.h2('Service Resource')
340
341 def _add_description(self, section):
342 official_service_name = get_official_service_name(self._service_model)
343 section.write(f'A resource representing {official_service_name}')
344
345 def _add_example(self, section, identifier_names):
346 section.style.start_codeblock()
347 section.style.new_line()
348 section.write('import boto3')
349 section.style.new_line()
350 section.style.new_line()
351 section.write(
352 f'{self._service_name} = boto3.resource(\'{self._service_name}\')'
353 )
354 section.style.end_codeblock()