Use pydoc for formatting docstrings

pydoc is part of the standard library and is much more robust at formatting
docstings than any trivial hand-rolled munger could be. For example, it
correctly preserves indentation within the docstring. Use it when
generating descriptions from docstrings for the API.

Change-Id: Ib565a64d990a45c652a9475255c37805f86070b4
This commit is contained in:
Zane Bitter 2016-09-20 10:56:54 -04:00
parent d42ce99218
commit 331df3abe8
5 changed files with 23 additions and 29 deletions

View File

@ -625,10 +625,3 @@ def format_snapshot(snapshot):
heat_timeutils.isotime(snapshot.created_at),
}
return result
def build_resource_description(docstring):
if docstring is not None:
return '\n'.join(map(lambda x: x.strip(), docstring.split('\n')))
else:
return _('No description given')

View File

@ -636,18 +636,18 @@ class ResourceRegistry(object):
return (version is None or
cls.get_class().support_status.version == version)
def resource_description(name, cls, with_description):
import heat.engine.resource
def resource_description(name, info, with_description):
if not with_description:
return name
if cls.description == 'Plugin':
rsrc = cls.value
elif cls.description == 'Template':
rsrc = cls.get_class()
else:
rsrc = None
rsrc_cls = info.get_class()
if rsrc_cls is None:
rsrc_cls = heat.engine.resource.Resource
return {
'resource_type': name,
'description': rsrc.__doc__}
'description': rsrc_cls.getdoc(),
}
return [resource_description(name, cls, with_description)
for name, cls in six.iteritems(self._registry)

View File

@ -14,6 +14,7 @@
import base64
import contextlib
import datetime as dt
import pydoc
import weakref
from oslo_config import cfg
@ -310,6 +311,12 @@ class Resource(object):
def external_id(self):
return self.t.external_id()
@classmethod
def getdoc(cls):
if cls.__doc__ is None:
return _('No description available')
return pydoc.getdoc(cls)
@property
def stack(self):
stack = self._stackref()

View File

@ -16,6 +16,7 @@ import datetime
import functools
import itertools
import os
import pydoc
import socket
import eventlet
@ -1533,10 +1534,6 @@ class EngineService(service.ServiceBase):
type_name=type_name,
version=heat_version,
with_description=with_description)
if with_description:
for resource_type in result:
resource_type['description'] = api.build_resource_description(
resource_type['description'])
return result
def list_template_versions(self, cnxt):
@ -1591,10 +1588,7 @@ class EngineService(service.ServiceBase):
functions = []
for func_name, func in six.iteritems(supported_funcs):
if func is not hot_functions.Removed:
if func.__doc__.split('\n')[0]:
desc = func.__doc__.split('\n')[0].strip()
else:
desc = func.__doc__.split('\n')[1].strip()
desc = pydoc.splitdoc(pydoc.getdoc(func))[0]
functions.append(
{'functions': func_name,
'description': desc}
@ -1617,6 +1611,8 @@ class EngineService(service.ServiceBase):
type_name)
raise exception.InvalidGlobalResource(type_name=type_name)
assert resource_class is not None
if resource_class.support_status.status == support.HIDDEN:
raise exception.NotSupported(feature=type_name)
@ -1657,9 +1653,7 @@ class EngineService(service.ServiceBase):
resource_class.support_status.to_dict()
}
if with_description:
docstring = resource_class.__doc__
description = api.build_resource_description(docstring)
result[rpc_api.RES_SCHEMA_DESCRIPTION] = description
result[rpc_api.RES_SCHEMA_DESCRIPTION] = resource_class.getdoc()
return result
def generate_template(self, cnxt, type_name, template_type='cfn'):

View File

@ -76,14 +76,14 @@ class ResourceTypeTest(common.HeatTestCase):
description = ("Heat Template Resource for Designate Domain.\n\n"
"Designate provides DNS-as-a-Service services for "
"OpenStack. So, domain\nis a realm with an "
"identification string, unique in DNS.\n")
"identification string, unique in DNS.")
self.assertIn({'resource_type': 'OS::Designate::Domain',
'description': description}, resources)
self.assertIn({'resource_type': 'AWS::RDS::DBInstance',
'description': 'Builtin AWS::RDS::DBInstance'},
resources)
self.assertIn({'resource_type': 'AWS::EC2::Instance',
'description': 'No description given'},
'description': 'No description available'},
resources)
def test_resource_schema(self):
@ -117,7 +117,7 @@ class ResourceTypeTest(common.HeatTestCase):
'message': None,
'previous_status': None
},
'description': 'No description given'
'description': 'No description available'
}
schema = self.eng.resource_schema(self.ctx, type_name=type_name,