Merged in dhellmann/wsme-sphinx (pull request #5)

This commit is contained in:
Christophe de Vienne 2012-12-11 09:25:50 +01:00
commit f9b76d3a5d
5 changed files with 123 additions and 36 deletions

View File

@ -3,6 +3,7 @@ import sphinx
import os.path import os.path
import wsme.types import wsme.types
from wsme import sphinxext
docpath = os.path.join( docpath = os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
@ -24,3 +25,21 @@ class TestSphinxExt(unittest.TestCase):
'-d', '.test_sphinxext/doctree', '-d', '.test_sphinxext/doctree',
docpath, docpath,
'.test_sphinxext/html']) == 0 '.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)')

View File

@ -1,5 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
import functools
import inspect import inspect
import sys import sys
@ -57,6 +58,7 @@ def wsexpose(*args, **kwargs):
funcdef = wsme.api.FunctionDefinition.get(f) funcdef = wsme.api.FunctionDefinition.get(f)
funcdef.resolve_types(wsme.types.registry) funcdef.resolve_types(wsme.types.registry)
@functools.wraps(f)
def callfunction(self, *args, **kwargs): def callfunction(self, *args, **kwargs):
try: try:
args, kwargs = wsme.rest.args.get_args( args, kwargs = wsme.rest.args.get_args(

View File

@ -27,6 +27,11 @@ field_re = re.compile(r':(?P<field>\w+)(\s+(?P<name>\w+))?:')
def datatypename(datatype): def datatypename(datatype):
if isinstance(datatype, wsme.types.UserType): if isinstance(datatype, wsme.types.UserType):
return datatype.name 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__ return datatype.__name__
@ -157,15 +162,36 @@ 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): class TypeDocumenter(autodoc.ClassDocumenter):
objtype = 'type' objtype = 'type'
directivetype = 'type' directivetype = 'type'
domain = 'wsme' domain = 'wsme'
required_arguments = 1 required_arguments = 1
default_samples_slot = 'after-docstring'
option_spec = dict(autodoc.ClassDocumenter.option_spec, **{ option_spec = dict(
'protocols': lambda l: [v.strip() for v in l.split(',')] autodoc.ClassDocumenter.option_spec,
**{'protocols': lambda l: [v.strip() for v in l.split(',')],
'samples-slot': check_samples_slot,
}) })
@classmethod @classmethod
@ -194,6 +220,16 @@ class TypeDocumenter(autodoc.ClassDocumenter):
return False return False
def add_content(self, more_content, no_docstring=False): def add_content(self, more_content, no_docstring=False):
# Check where to include the samples
samples_slot = self.options.samples_slot or self.default_samples_slot
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( protocols = get_protocols(
self.options.protocols or self.env.app.config.wsme_protocols self.options.protocols or self.env.app.config.wsme_protocols
) )
@ -220,8 +256,15 @@ class TypeDocumenter(autodoc.ClassDocumenter):
self.add_line(line, u'<wsme.sphinxext') self.add_line(line, u'<wsme.sphinxext')
self.add_line(u'', '<wsme.sphinxext>') self.add_line(u'', '<wsme.sphinxext>')
super(TypeDocumenter, self).add_content(
more_content, no_docstring) 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): class AttributeDocumenter(autodoc.AttributeDocumenter):
@ -341,13 +384,14 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
@classmethod @classmethod
def can_document_member(cls, member, membername, isattr, parent): 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): def import_object(self):
ret = super(FunctionDocumenter, self).import_object() ret = super(FunctionDocumenter, self).import_object()
self.directivetype = 'function' self.directivetype = 'function'
self.wsme_fd = wsme.api.FunctionDefinition.get(self.object) 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 return ret
def format_args(self): def format_args(self):
@ -360,6 +404,10 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
"""Inject the type and param fields into the docstrings so that the """Inject the type and param fields into the docstrings so that the
user can add its own param fields to document the parameters""" user can add its own param fields to document the parameters"""
docstrings = super(FunctionDocumenter, self).get_doc(encoding) 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() found_params = set()
protocols = get_protocols( protocols = get_protocols(
@ -390,14 +438,13 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
and m.group('name') == arg.name: and m.group('name') == arg.name:
pos = (si, i + 1) pos = (si, i + 1)
break break
break
docstring = docstrings[pos[0]] docstring = docstrings[pos[0]]
docstring[pos[1]:pos[1]] = content docstring[pos[1]:pos[1]] = content
next_param_pos = (pos[0], pos[1] + len(content)) next_param_pos = (pos[0], pos[1] + len(content))
if self.wsme_fd.return_type: if self.wsme_fd.return_type:
content = [ content = [
u':rtype: %s' % self.wsme_fd.return_type.__name__ u':rtype: %s' % datatypename(self.wsme_fd.return_type)
] ]
pos = None pos = None
for si, docstring in enumerate(docstrings): for si, docstring in enumerate(docstrings):
@ -406,8 +453,7 @@ class FunctionDocumenter(autodoc.MethodDocumenter):
if m and m.group('field') == 'return': if m and m.group('field') == 'return':
pos = (si, i + 1) pos = (si, i + 1)
break break
break else:
if pos is None:
pos = next_param_pos pos = next_param_pos
docstring = docstrings[pos[0]] docstring = docstrings[pos[0]]
docstring[pos[1]:pos[1]] = content docstring[pos[1]:pos[1]] = content

View File

@ -355,3 +355,15 @@ class TestTypes(unittest.TestCase):
def test_array_eq(self): def test_array_eq(self):
l = [types.ArrayType(str)] l = [types.ArrayType(str)]
assert types.ArrayType(str) in l 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 == {'': ''}

View File

@ -34,6 +34,9 @@ class ArrayType(object):
return isinstance(other, ArrayType) \ return isinstance(other, ArrayType) \
and self.item_type == other.item_type and self.item_type == other.item_type
def sample(self):
return [getattr(self.item_type, 'sample', self.item_type)()]
@property @property
def item_type(self): def item_type(self):
if isinstance(self._item_type, weakref.ref): if isinstance(self._item_type, weakref.ref):
@ -67,6 +70,11 @@ class DictType(object):
def __hash__(self): def __hash__(self):
return hash((self.key_type, self.value_type)) 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 @property
def value_type(self): def value_type(self):
if isinstance(self._value_type, weakref.ref): if isinstance(self._value_type, weakref.ref):