@@ -61,6 +61,32 @@ def py_type_name(type_name):
61
61
}.get (type_name , type_name )
62
62
63
63
64
+ def py_default (type_name ):
65
+ """
66
+ Get the Python default value for a given model type, useful
67
+ for generated examples.
68
+
69
+ >>> py_default('string')
70
+ '\' string\' '
71
+ >>> py_default('list')
72
+ '[...]'
73
+ >>> py_default('unknown')
74
+ '...'
75
+
76
+ :rtype: string
77
+ """
78
+ return {
79
+ 'double' : '123.0' ,
80
+ 'long' : '123' ,
81
+ 'integer' : '123' ,
82
+ 'string' : "'string'" ,
83
+ 'blob' : "b'bytes'" ,
84
+ 'list' : '[...]' ,
85
+ 'map' : '{...}' ,
86
+ 'structure' : '{...}' ,
87
+ 'timestamp' : 'datetime(2015, 1, 1)' ,
88
+ }.get (type_name , '...' )
89
+
64
90
def html_to_rst (html , indent = 0 , indentFirst = False ):
65
91
"""
66
92
Use bcdoc to convert html to rst.
@@ -105,7 +131,18 @@ def docs_for(service_name):
105
131
106
132
print ('Processing {0}-{1}' .format (service_name , service_model .api_version ))
107
133
134
+ # The following creates an official name of 'Amazon Simple Storage
135
+ # Service (S3)' our of 'Amazon Simple Storage Service' and 'Amazon S3'.
136
+ # It tries to be smart, so for `Amazon DynamoDB' and 'DynamoDB' we would
137
+ # get an official name of 'Amazon DynamoDB'.
108
138
official_name = service_model .metadata .get ('serviceFullName' )
139
+ short_name = service_model .metadata .get ('serviceAbbreviation' , '' )
140
+ if short_name .startswith ('Amazon' ):
141
+ short_name = short_name [7 :]
142
+ if short_name .startswith ('AWS' ):
143
+ short_name = short_name [4 :]
144
+ if short_name and short_name .lower () not in official_name .lower ():
145
+ official_name += ' ({0})' .format (short_name )
109
146
110
147
docs = '{0}\n {1}\n \n ' .format (official_name , '=' * len (official_name ))
111
148
@@ -116,18 +153,47 @@ def docs_for(service_name):
116
153
filename = (os .path .dirname (__file__ ) + '/data/resources/'
117
154
'{0}-{1}.resources.json' ).format (service_name ,
118
155
service_model .api_version )
156
+ # We can't use a set here because dicts aren't hashable!
157
+ models = {}
119
158
if os .path .exists (filename ):
120
159
data = json .load (open (filename ))
121
160
model = ResourceModel (service_name , data ['service' ], data ['resources' ])
122
161
162
+ for collection_model in model .collections :
163
+ collection_model .parent_name = model .name
164
+ models [collection_model .name ] = {
165
+ 'type' : 'collection' ,
166
+ 'model' : collection_model
167
+ }
168
+
123
169
docs += document_resource (service_name , official_name , model ,
124
170
service_model )
125
171
172
+ # First, collect all the models...
126
173
for name , model in sorted (data ['resources' ].items (),
127
174
key = lambda i :i [0 ]):
128
175
resource_model = ResourceModel (name , model , data ['resources' ])
129
- docs += document_resource (service_name , official_name ,
130
- resource_model , service_model )
176
+ if name not in models :
177
+ models [name ] = {'type' : 'resource' , 'model' : resource_model }
178
+
179
+ for collection_model in resource_model .collections :
180
+ collection_model .parent_name = xform_name (resource_model .name )
181
+
182
+ cname = collection_model .name + 'CollectionManager'
183
+ if cname not in models :
184
+ models [cname ] = {'type' : 'collection' ,
185
+ 'model' : collection_model }
186
+
187
+ # Then render them out in alphabetical order.
188
+ for name , item in sorted (models .items ()):
189
+ model = item ['model' ]
190
+ if item ['type' ] == 'resource' :
191
+ docs += document_resource (service_name , official_name ,
192
+ model , service_model )
193
+ elif item ['type' ] == 'collection' :
194
+ docs += document_collection (
195
+ service_name , official_name , model ,
196
+ model .resource .model , service_model )
131
197
132
198
return docs
133
199
@@ -169,7 +235,8 @@ def document_client(service_name, official_name, service_model):
169
235
operation = service_model .operation_model (operation_name )
170
236
docs += document_operation (
171
237
operation , service_name ,
172
- paginated = client .can_paginate (xform_name (operation_name )))
238
+ paginated = client .can_paginate (xform_name (operation_name )),
239
+ example_instance = 'client' , example_response = 'response' )
173
240
174
241
return docs
175
242
@@ -293,16 +360,53 @@ def document_resource(service_name, official_name, resource_model,
293
360
for collection in sorted (resource_model .collections ,
294
361
key = lambda i : i .name ):
295
362
docs += (' .. py:attribute:: {0}\n \n '
296
- '(:py:class:`~boto3.resources.collection. CollectionManager`)'
297
- ' A collection of :py:class:`{1 }.{2 }` instances. This'
298
- ' collection uses the :py:meth:`{3 }.Client.{4 }` operation'
363
+ '(:py:class:`{1}.{2} CollectionManager`)'
364
+ ' A collection of :py:class:`{3 }.{4 }` instances. This'
365
+ ' collection uses the :py:meth:`{5 }.Client.{6 }` operation'
299
366
' to get items.\n \n ' ).format (
300
367
xform_name (collection .name ), service_name ,
368
+ collection .name , service_name ,
301
369
collection .resource .type , service_name ,
302
370
xform_name (collection .request .operation ))
303
371
304
372
return docs
305
373
374
+ def document_collection (service_name , official_name , collection_model ,
375
+ resource_model , service_model ):
376
+ """
377
+ Generate reference documentation about a collection and any
378
+ batch actions it might have.
379
+ """
380
+ title = collection_model .name + 'Collection'
381
+ docs = '{0}\n {1}\n \n ' .format (title , '-' * len (title ))
382
+ docs += '.. py:class:: {0}.{1}CollectionManager()\n \n ' .format (
383
+ service_name , collection_model .name )
384
+ docs += (' A collection of :py:class:`{0}.{1}` resources for {2}. See'
385
+ ' the'
386
+ ' :py:class:`~boto3.resources.collection.CollectionManager`'
387
+ ' base class for additional methods.\n \n '
388
+ ' This collection uses the :py:meth:`{3}.Client.{4}`'
389
+ ' operation to get items, and its parameters can be'
390
+ ' used as filters::\n \n ' ).format (
391
+ service_name , resource_model .name , official_name ,
392
+ service_name , xform_name (collection_model .request .operation ))
393
+ docs += (' for {0} in {1}.{2}.all():\n '
394
+ ' print({0})\n \n ' ).format (
395
+ xform_name (collection_model .resource .type ),
396
+ collection_model .parent_name ,
397
+ xform_name (collection_model .name ),
398
+ xform_name (collection_model .resource .type ))
399
+
400
+ if collection_model .batch_actions :
401
+ docs += (' .. rst-class:: admonition-title\n \n Batch Actions\n \n '
402
+ ' Batch actions provide a way to manipulate groups of'
403
+ ' resources in a single service operation call.\n \n ' )
404
+ for action in sorted (collection_model .batch_actions , key = lambda i :i .name ):
405
+ docs += document_action (action , service_name , resource_model ,
406
+ service_model )
407
+
408
+ return docs
409
+
306
410
def document_action (action , service_name , resource_model , service_model ,
307
411
action_type = 'action' ):
308
412
"""
@@ -316,29 +420,34 @@ def document_action(action, service_name, resource_model, service_model,
316
420
print ('Cannot get operation ' + action .request .operation )
317
421
return ''
318
422
319
- ignore_params = [p .target for p in action .request .params ]
423
+ # Here we split because we only care about top-level parameter names
424
+ ignore_params = [p .target .split ('.' )[0 ] for p in action .request .params ]
320
425
321
426
rtype = 'dict'
322
427
if action_type == 'action' :
323
428
description = (' This method calls'
324
429
' :py:meth:`{0}.Client.{1}`.' ).format (
325
430
service_name ,
326
431
xform_name (action .request .operation ))
432
+ example_response = 'response'
327
433
if action .resource :
328
434
rtype = ':py:class:`{0}.{1}`' .format (
329
435
service_name , action .resource .type )
436
+ example_response = xform_name (action .resource .type )
330
437
331
438
# Is the response plural? If so we are returning a list!
332
439
if action .path and '[]' in action .path :
333
440
rtype = 'list({0})' .format (rtype )
334
441
335
442
return document_operation (
336
443
operation_model , service_name , operation_name = xform_name (action .name ),
337
- description = description , ignore_params = ignore_params , rtype = rtype )
444
+ description = description , ignore_params = ignore_params , rtype = rtype ,
445
+ example_instance = service_name , example_response = example_response )
338
446
339
447
def document_operation (operation_model , service_name , operation_name = None ,
340
448
description = None , ignore_params = None , rtype = 'dict' ,
341
- paginated = False ):
449
+ paginated = False , example_instance = None ,
450
+ example_response = None ):
342
451
"""
343
452
Document an operation. The description can be overridden and certain
344
453
params hidden to support documenting resource actions.
@@ -361,7 +470,7 @@ def document_operation(operation_model, service_name, operation_name=None,
361
470
optional_params = [k for k in params .keys () if k not in required and \
362
471
k not in ignore_params ]
363
472
param_desc = ', ' .join ([
364
- ', ' .join (required_params ),
473
+ ', ' .join ([ '{0}=None' . format ( k ) for k in required_params ] ),
365
474
', ' .join (['{0}=None' .format (k ) for k in optional_params ])
366
475
])
367
476
@@ -379,13 +488,28 @@ def document_operation(operation_model, service_name, operation_name=None,
379
488
if paginated :
380
489
docs += ' This operation can be paginated.\n \n '
381
490
491
+ if example_instance :
492
+ dummy_params = []
493
+ for key , value in params .items ():
494
+ if key in ignore_params :
495
+ continue
496
+ if key in required_params :
497
+ default = py_default (value .type_name )
498
+ dummy_params .append ('{0}={1}' .format (
499
+ key , default ))
500
+ docs += ' Example::\n \n {0} = {1}.{2}({3})\n \n ' .format (
501
+ example_response , example_instance , operation_name ,
502
+ ', ' .join (dummy_params ))
503
+
382
504
for key , value in params .items ():
383
505
# Skip identifiers as these are automatically set!
384
506
if key in ignore_params :
385
507
continue
386
508
param_type = py_type_name (value .type_name )
387
- docs += (' :param {0} {1}: {2}\n ' .format (
388
- param_type , key , html_to_rst (value .documentation , indent = 9 )))
509
+ required = key in required_params and 'Required' or 'Optional'
510
+ docs += (' :param {0} {1}: *{2}* - {3}\n ' .format (
511
+ param_type , key , required ,
512
+ html_to_rst (value .documentation , indent = 9 )))
389
513
390
514
docs += '\n \n :rtype: {0}\n \n ' .format (rtype )
391
515
0 commit comments