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:
parent
d42ce99218
commit
331df3abe8
|
@ -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')
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue