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), heat_timeutils.isotime(snapshot.created_at),
} }
return result 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 return (version is None or
cls.get_class().support_status.version == version) 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: if not with_description:
return name return name
if cls.description == 'Plugin': rsrc_cls = info.get_class()
rsrc = cls.value if rsrc_cls is None:
elif cls.description == 'Template': rsrc_cls = heat.engine.resource.Resource
rsrc = cls.get_class()
else:
rsrc = None
return { return {
'resource_type': name, 'resource_type': name,
'description': rsrc.__doc__} 'description': rsrc_cls.getdoc(),
}
return [resource_description(name, cls, with_description) return [resource_description(name, cls, with_description)
for name, cls in six.iteritems(self._registry) for name, cls in six.iteritems(self._registry)

View File

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

View File

@ -16,6 +16,7 @@ import datetime
import functools import functools
import itertools import itertools
import os import os
import pydoc
import socket import socket
import eventlet import eventlet
@ -1533,10 +1534,6 @@ class EngineService(service.ServiceBase):
type_name=type_name, type_name=type_name,
version=heat_version, version=heat_version,
with_description=with_description) with_description=with_description)
if with_description:
for resource_type in result:
resource_type['description'] = api.build_resource_description(
resource_type['description'])
return result return result
def list_template_versions(self, cnxt): def list_template_versions(self, cnxt):
@ -1591,10 +1588,7 @@ class EngineService(service.ServiceBase):
functions = [] functions = []
for func_name, func in six.iteritems(supported_funcs): for func_name, func in six.iteritems(supported_funcs):
if func is not hot_functions.Removed: if func is not hot_functions.Removed:
if func.__doc__.split('\n')[0]: desc = pydoc.splitdoc(pydoc.getdoc(func))[0]
desc = func.__doc__.split('\n')[0].strip()
else:
desc = func.__doc__.split('\n')[1].strip()
functions.append( functions.append(
{'functions': func_name, {'functions': func_name,
'description': desc} 'description': desc}
@ -1617,6 +1611,8 @@ class EngineService(service.ServiceBase):
type_name) type_name)
raise exception.InvalidGlobalResource(type_name=type_name) raise exception.InvalidGlobalResource(type_name=type_name)
assert resource_class is not None
if resource_class.support_status.status == support.HIDDEN: if resource_class.support_status.status == support.HIDDEN:
raise exception.NotSupported(feature=type_name) raise exception.NotSupported(feature=type_name)
@ -1657,9 +1653,7 @@ class EngineService(service.ServiceBase):
resource_class.support_status.to_dict() resource_class.support_status.to_dict()
} }
if with_description: if with_description:
docstring = resource_class.__doc__ result[rpc_api.RES_SCHEMA_DESCRIPTION] = resource_class.getdoc()
description = api.build_resource_description(docstring)
result[rpc_api.RES_SCHEMA_DESCRIPTION] = description
return result return result
def generate_template(self, cnxt, type_name, template_type='cfn'): 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" description = ("Heat Template Resource for Designate Domain.\n\n"
"Designate provides DNS-as-a-Service services for " "Designate provides DNS-as-a-Service services for "
"OpenStack. So, domain\nis a realm with an " "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', self.assertIn({'resource_type': 'OS::Designate::Domain',
'description': description}, resources) 'description': description}, resources)
self.assertIn({'resource_type': 'AWS::RDS::DBInstance', self.assertIn({'resource_type': 'AWS::RDS::DBInstance',
'description': 'Builtin AWS::RDS::DBInstance'}, 'description': 'Builtin AWS::RDS::DBInstance'},
resources) resources)
self.assertIn({'resource_type': 'AWS::EC2::Instance', self.assertIn({'resource_type': 'AWS::EC2::Instance',
'description': 'No description given'}, 'description': 'No description available'},
resources) resources)
def test_resource_schema(self): def test_resource_schema(self):
@ -117,7 +117,7 @@ class ResourceTypeTest(common.HeatTestCase):
'message': None, 'message': None,
'previous_status': None 'previous_status': None
}, },
'description': 'No description given' 'description': 'No description available'
} }
schema = self.eng.resource_schema(self.ctx, type_name=type_name, schema = self.eng.resource_schema(self.ctx, type_name=type_name,