mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
#437: autodoc now shows values of class data attributes.
This commit is contained in:
parent
b4558269a6
commit
dc0c43da9a
2
CHANGES
2
CHANGES
@ -85,6 +85,8 @@ Release 1.1 (in development)
|
||||
|
||||
* #537: Added :confval:`nitpick_ignore`.
|
||||
|
||||
* #437: autodoc now shows values of class data attributes.
|
||||
|
||||
* autodoc now supports documenting the signatures of ``functools.partial``
|
||||
objects.
|
||||
|
||||
|
@ -88,6 +88,7 @@ class PyObject(ObjectDescription):
|
||||
option_spec = {
|
||||
'noindex': directives.flag,
|
||||
'module': directives.unchanged,
|
||||
'annotation': directives.unchanged,
|
||||
}
|
||||
|
||||
doc_field_types = [
|
||||
@ -180,6 +181,8 @@ class PyObject(ObjectDescription):
|
||||
nodetext = modname + '.'
|
||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||
|
||||
anno = self.options.get('annotation')
|
||||
|
||||
signode += addnodes.desc_name(name, name)
|
||||
if not arglist:
|
||||
if self.needs_arglist():
|
||||
@ -187,10 +190,15 @@ class PyObject(ObjectDescription):
|
||||
signode += addnodes.desc_parameterlist()
|
||||
if retann:
|
||||
signode += addnodes.desc_returns(retann, retann)
|
||||
if anno:
|
||||
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno)
|
||||
return fullname, name_prefix
|
||||
|
||||
_pseudo_parse_arglist(signode, arglist)
|
||||
if retann:
|
||||
signode += addnodes.desc_returns(retann, retann)
|
||||
if anno:
|
||||
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno)
|
||||
return fullname, name_prefix
|
||||
|
||||
def get_index_text(self, modname, name):
|
||||
|
@ -28,7 +28,7 @@ from sphinx.application import ExtensionError
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
|
||||
safe_getattr
|
||||
safe_getattr, safe_repr
|
||||
from sphinx.util.pycompat import base_exception, class_types
|
||||
from sphinx.util.docstrings import prepare_docstring
|
||||
|
||||
@ -566,9 +566,15 @@ class Documenter(object):
|
||||
skip = False
|
||||
isattr = True
|
||||
else:
|
||||
# ignore undocumented members if :undoc-members:
|
||||
# is not given
|
||||
# ignore undocumented members if :undoc-members: is not given
|
||||
doc = self.get_attr(member, '__doc__', None)
|
||||
# if the member __doc__ is the same as self's __doc__, it's just
|
||||
# inherited and therefore not the member's doc
|
||||
cls = self.get_attr(member, '__class__', None)
|
||||
if cls:
|
||||
cls_doc = self.get_attr(cls, '__doc__', None)
|
||||
if cls_doc == doc:
|
||||
doc = None
|
||||
skip = not self.options.undoc_members and not doc
|
||||
|
||||
# give the user a chance to decide whether this member
|
||||
@ -1058,11 +1064,21 @@ class DataDocumenter(ModuleLevelDocumenter):
|
||||
"""
|
||||
objtype = 'data'
|
||||
member_order = 40
|
||||
priority = -10
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
return isinstance(parent, ModuleDocumenter) and isattr
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
ModuleLevelDocumenter.add_directive_header(self, sig)
|
||||
try:
|
||||
objrepr = safe_repr(self.object)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.add_line(u' :annotation: = ' + objrepr, '<autodoc>')
|
||||
|
||||
def document_members(self, all_members=False):
|
||||
pass
|
||||
|
||||
@ -1144,16 +1160,44 @@ class AttributeDocumenter(ClassLevelDocumenter):
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
isdatadesc = isdescriptor(member) and not \
|
||||
isinstance(member, cls.method_types)
|
||||
return isdatadesc or \
|
||||
(isattr and not isinstance(parent, ModuleDocumenter))
|
||||
return isdatadesc or (not isinstance(parent, ModuleDocumenter)
|
||||
and not inspect.isroutine(member)
|
||||
and not isinstance(member, class_types))
|
||||
|
||||
def document_members(self, all_members=False):
|
||||
pass
|
||||
|
||||
def import_object(self):
|
||||
ret = ClassLevelDocumenter.import_object(self)
|
||||
if isdescriptor(self.object) and \
|
||||
not isinstance(self.object, self.method_types):
|
||||
self._datadescriptor = True
|
||||
else:
|
||||
# if it's not a data descriptor
|
||||
self._datadescriptor = False
|
||||
return ret
|
||||
|
||||
def get_real_modname(self):
|
||||
return self.get_attr(self.parent or self.object, '__module__', None) \
|
||||
or self.modname
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
ClassLevelDocumenter.add_directive_header(self, sig)
|
||||
if not self._datadescriptor:
|
||||
try:
|
||||
objrepr = safe_repr(self.object)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.add_line(u' :annotation: = ' + objrepr, '<autodoc>')
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
if not self._datadescriptor:
|
||||
# if it's not a data descriptor, its docstring is very probably the
|
||||
# wrong thing to display
|
||||
no_docstring = True
|
||||
ClassLevelDocumenter.add_content(self, more_content, no_docstring)
|
||||
|
||||
|
||||
class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
"""
|
||||
@ -1176,6 +1220,7 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
"""Never import anything."""
|
||||
# disguise as an attribute
|
||||
self.objtype = 'attribute'
|
||||
self._datadescriptor = False
|
||||
return True
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
|
@ -79,3 +79,12 @@ def safe_getmembers(object, predicate=None):
|
||||
results.append((key, value))
|
||||
results.sort()
|
||||
return results
|
||||
|
||||
|
||||
def safe_repr(object):
|
||||
"""A repr() implementation that returns text safe to use in reST context."""
|
||||
try:
|
||||
s = repr(object)
|
||||
except Exception:
|
||||
raise ValueError
|
||||
return s.replace('\n', ' ')
|
||||
|
@ -341,6 +341,7 @@ def test_generate():
|
||||
inst = AutoDirective._registry[objtype](directive, name)
|
||||
inst.generate(**kw)
|
||||
assert directive.result
|
||||
#print '\n'.join(directive.result)
|
||||
assert len(_warnings) == 0, _warnings
|
||||
del directive.result[:]
|
||||
|
||||
@ -430,7 +431,8 @@ def test_generate():
|
||||
options.members = ALL
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.undoc_members = True
|
||||
should.extend((('method', 'test_autodoc.Class.undocmeth'),
|
||||
should.extend((('attribute', 'test_autodoc.Class.skipattr'),
|
||||
('method', 'test_autodoc.Class.undocmeth'),
|
||||
('method', 'test_autodoc.Class.roger')))
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.inherited_members = True
|
||||
@ -519,11 +521,24 @@ def test_generate():
|
||||
'.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
|
||||
'test_autodoc.Class.moore')
|
||||
|
||||
# test new attribute documenter behavior
|
||||
directive.env.temp_data['py:module'] = 'test_autodoc'
|
||||
options.undoc_members = True
|
||||
assert_processes([('class', 'test_autodoc.AttCls'),
|
||||
('attribute', 'test_autodoc.AttCls.a1'),
|
||||
('attribute', 'test_autodoc.AttCls.a2'),
|
||||
], 'class', 'AttCls')
|
||||
assert_result_contains(
|
||||
' :annotation: = hello world', 'attribute', 'AttCls.a1')
|
||||
assert_result_contains(
|
||||
' :annotation: = None', 'attribute', 'AttCls.a2')
|
||||
|
||||
|
||||
# --- generate fodder ------------
|
||||
|
||||
__all__ = ['Class']
|
||||
|
||||
#: documentation for the integer
|
||||
integer = 1
|
||||
|
||||
class CustomEx(Exception):
|
||||
@ -642,3 +657,11 @@ First line of docstring
|
||||
|
||||
rest of docstring
|
||||
"""
|
||||
|
||||
class StrRepr(str):
|
||||
def __repr__(self):
|
||||
return self
|
||||
|
||||
class AttCls(object):
|
||||
a1 = StrRepr('hello\nworld')
|
||||
a2 = None
|
||||
|
Loading…
Reference in New Issue
Block a user