Merged in dhellmann/wsme-sphinx (pull request #5)
This commit is contained in:
commit
f9b76d3a5d
@ -3,6 +3,7 @@ import sphinx
|
||||
import os.path
|
||||
|
||||
import wsme.types
|
||||
from wsme import sphinxext
|
||||
|
||||
docpath = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
@ -24,3 +25,21 @@ class TestSphinxExt(unittest.TestCase):
|
||||
'-d', '.test_sphinxext/doctree',
|
||||
docpath,
|
||||
'.test_sphinxext/html']) == 0
|
||||
|
||||
|
||||
class TestDataTypeName(unittest.TestCase):
|
||||
def test_user_type(self):
|
||||
self.assertEqual(sphinxext.datatypename(ASampleType),
|
||||
'ASampleType')
|
||||
|
||||
def test_dict_type(self):
|
||||
d = wsme.types.DictType(str, str)
|
||||
self.assertEqual(sphinxext.datatypename(d), 'dict(str: str)')
|
||||
d = wsme.types.DictType(str, ASampleType)
|
||||
self.assertEqual(sphinxext.datatypename(d), 'dict(str: ASampleType)')
|
||||
|
||||
def test_array_type(self):
|
||||
d = wsme.types.ArrayType(str)
|
||||
self.assertEqual(sphinxext.datatypename(d), 'list(str)')
|
||||
d = wsme.types.ArrayType(ASampleType)
|
||||
self.assertEqual(sphinxext.datatypename(d), 'list(ASampleType)')
|
||||
|
@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
@ -57,6 +58,7 @@ def wsexpose(*args, **kwargs):
|
||||
funcdef = wsme.api.FunctionDefinition.get(f)
|
||||
funcdef.resolve_types(wsme.types.registry)
|
||||
|
||||
@functools.wraps(f)
|
||||
def callfunction(self, *args, **kwargs):
|
||||
try:
|
||||
args, kwargs = wsme.rest.args.get_args(
|
||||
|
@ -27,6 +27,11 @@ field_re = re.compile(r':(?P<field>\w+)(\s+(?P<name>\w+))?:')
|
||||
def datatypename(datatype):
|
||||
if isinstance(datatype, wsme.types.UserType):
|
||||
return datatype.name
|
||||
if isinstance(datatype, wsme.types.DictType):
|
||||
return 'dict(%s: %s)' % (datatypename(datatype.key_type),
|
||||
datatypename(datatype.value_type))
|
||||
if isinstance(datatype, wsme.types.ArrayType):
|
||||
return 'list(%s)' % datatypename(datatype.item_type)
|
||||
return datatype.__name__
|
||||
|
||||
|
||||
@ -157,16 +162,37 @@ class AttributeDirective(PyClassmember):
|
||||
]
|
||||
|
||||
|
||||
def check_samples_slot(value):
|
||||
"""Validate the samples_slot option to the TypeDocumenter.
|
||||
|
||||
Valid positions are 'before-docstring' and
|
||||
'after-docstring'. Using the explicit 'none' disables sample
|
||||
output. The default is after-docstring.
|
||||
"""
|
||||
if not value:
|
||||
return 'after-docstring'
|
||||
val = directives.choice(
|
||||
value,
|
||||
('none', # do not include
|
||||
'before-docstring', # show samples then docstring
|
||||
'after-docstring', # show docstring then samples
|
||||
))
|
||||
return val
|
||||
|
||||
|
||||
class TypeDocumenter(autodoc.ClassDocumenter):
|
||||
objtype = 'type'
|
||||
directivetype = 'type'
|
||||
domain = 'wsme'
|
||||
|
||||
required_arguments = 1
|
||||
default_samples_slot = 'after-docstring'
|
||||
|
||||
option_spec = dict(autodoc.ClassDocumenter.option_spec, **{
|
||||
'protocols': lambda l: [v.strip() for v in l.split(',')]
|
||||
})
|
||||
option_spec = dict(
|
||||
autodoc.ClassDocumenter.option_spec,
|
||||
**{'protocols': lambda l: [v.strip() for v in l.split(',')],
|
||||
'samples-slot': check_samples_slot,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
@ -194,34 +220,51 @@ class TypeDocumenter(autodoc.ClassDocumenter):
|
||||
return False
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
protocols = get_protocols(
|
||||
self.options.protocols or self.env.app.config.wsme_protocols
|
||||
)
|
||||
content = []
|
||||
if protocols:
|
||||
sample_obj = make_sample_object(self.object)
|
||||
content.extend([
|
||||
l_(u'Data samples:'),
|
||||
u'',
|
||||
u'.. cssclass:: toggle',
|
||||
u''
|
||||
])
|
||||
for name, protocol in protocols:
|
||||
language, sample = protocol.encode_sample_value(
|
||||
self.object, sample_obj, format=True)
|
||||
content.extend([
|
||||
name,
|
||||
u' .. code-block:: ' + language,
|
||||
u'',
|
||||
])
|
||||
content.extend((
|
||||
u' ' * 8 + line for line in sample.split('\n')))
|
||||
for line in content:
|
||||
self.add_line(line, u'<wsme.sphinxext')
|
||||
# Check where to include the samples
|
||||
samples_slot = self.options.samples_slot or self.default_samples_slot
|
||||
|
||||
self.add_line(u'', '<wsme.sphinxext>')
|
||||
super(TypeDocumenter, self).add_content(
|
||||
more_content, no_docstring)
|
||||
print 'SAMPLES SLOT:', self.options.samples_slot
|
||||
|
||||
def add_docstring():
|
||||
super(TypeDocumenter, self).add_content(
|
||||
more_content, no_docstring)
|
||||
|
||||
def add_samples():
|
||||
protocols = get_protocols(
|
||||
self.options.protocols or self.env.app.config.wsme_protocols
|
||||
)
|
||||
content = []
|
||||
if protocols:
|
||||
sample_obj = make_sample_object(self.object)
|
||||
content.extend([
|
||||
l_(u'Data samples:'),
|
||||
u'',
|
||||
u'.. cssclass:: toggle',
|
||||
u''
|
||||
])
|
||||
for name, protocol in protocols:
|
||||
language, sample = protocol.encode_sample_value(
|
||||
self.object, sample_obj, format=True)
|
||||
content.extend([
|
||||
name,
|
||||
u' .. code-block:: ' + language,
|
||||
u'',
|
||||
])
|
||||
content.extend((
|
||||
u' ' * 8 + line for line in sample.split('\n')))
|
||||
for line in content:
|
||||
self.add_line(line, u'<wsme.sphinxext')
|
||||
|
||||
self.add_line(u'', '<wsme.sphinxext>')
|
||||
|
||||
if samples_slot == 'after-docstring':
|
||||
add_docstring()
|
||||
add_samples()
|
||||
elif samples_slot == 'before-docstring':
|
||||
add_samples()
|
||||
add_docstring()
|
||||
else:
|
||||
add_docstring()
|
||||
|
||||
|
||||
class AttributeDocumenter(autodoc.AttributeDocumenter):
|
||||
@ -341,13 +384,14 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
return wsme.api.iswsmefunction(member)
|
||||
return (isinstance(parent, ServiceDocumenter)
|
||||
and wsme.api.iswsmefunction(member))
|
||||
|
||||
def import_object(self):
|
||||
ret = super(FunctionDocumenter, self).import_object()
|
||||
self.directivetype = 'function'
|
||||
self.wsme_fd = wsme.api.FunctionDefinition.get(self.object)
|
||||
self.retann = self.wsme_fd.return_type.__name__
|
||||
self.retann = datatypename(self.wsme_fd.return_type)
|
||||
return ret
|
||||
|
||||
def format_args(self):
|
||||
@ -360,6 +404,10 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
|
||||
"""Inject the type and param fields into the docstrings so that the
|
||||
user can add its own param fields to document the parameters"""
|
||||
docstrings = super(FunctionDocumenter, self).get_doc(encoding)
|
||||
# If the function doesn't have a docstring, add an empty list
|
||||
# so the default behaviors below work correctly.
|
||||
if not docstrings:
|
||||
docstrings.append([])
|
||||
found_params = set()
|
||||
|
||||
protocols = get_protocols(
|
||||
@ -390,14 +438,13 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
|
||||
and m.group('name') == arg.name:
|
||||
pos = (si, i + 1)
|
||||
break
|
||||
break
|
||||
docstring = docstrings[pos[0]]
|
||||
docstring[pos[1]:pos[1]] = content
|
||||
next_param_pos = (pos[0], pos[1] + len(content))
|
||||
|
||||
if self.wsme_fd.return_type:
|
||||
content = [
|
||||
u':rtype: %s' % self.wsme_fd.return_type.__name__
|
||||
u':rtype: %s' % datatypename(self.wsme_fd.return_type)
|
||||
]
|
||||
pos = None
|
||||
for si, docstring in enumerate(docstrings):
|
||||
@ -406,8 +453,7 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
|
||||
if m and m.group('field') == 'return':
|
||||
pos = (si, i + 1)
|
||||
break
|
||||
break
|
||||
if pos is None:
|
||||
else:
|
||||
pos = next_param_pos
|
||||
docstring = docstrings[pos[0]]
|
||||
docstring[pos[1]:pos[1]] = content
|
||||
|
@ -355,3 +355,15 @@ class TestTypes(unittest.TestCase):
|
||||
def test_array_eq(self):
|
||||
l = [types.ArrayType(str)]
|
||||
assert types.ArrayType(str) in l
|
||||
|
||||
def test_array_sample(self):
|
||||
s = types.ArrayType(str).sample()
|
||||
assert isinstance(s, list)
|
||||
assert s
|
||||
assert s[0] == ''
|
||||
|
||||
def test_dict_sample(self):
|
||||
s = types.DictType(str, str).sample()
|
||||
assert isinstance(s, dict)
|
||||
assert s
|
||||
assert s == {'': ''}
|
||||
|
@ -34,6 +34,9 @@ class ArrayType(object):
|
||||
return isinstance(other, ArrayType) \
|
||||
and self.item_type == other.item_type
|
||||
|
||||
def sample(self):
|
||||
return [getattr(self.item_type, 'sample', self.item_type)()]
|
||||
|
||||
@property
|
||||
def item_type(self):
|
||||
if isinstance(self._item_type, weakref.ref):
|
||||
@ -67,6 +70,11 @@ class DictType(object):
|
||||
def __hash__(self):
|
||||
return hash((self.key_type, self.value_type))
|
||||
|
||||
def sample(self):
|
||||
key = getattr(self.key_type, 'sample', self.key_type)()
|
||||
value = getattr(self.value_type, 'sample', self.value_type)()
|
||||
return {key: value}
|
||||
|
||||
@property
|
||||
def value_type(self):
|
||||
if isinstance(self._value_type, weakref.ref):
|
||||
|
Loading…
x
Reference in New Issue
Block a user