mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #6320 from tk0miya/refactor_py_domain3
Support property (autodoc, py-domain)
This commit is contained in:
commit
e32a610d7a
8
CHANGES
8
CHANGES
@ -84,8 +84,12 @@ Features added
|
||||
imported members on autosummary
|
||||
* #6271: ``make clean`` is catastrophically broken if building into '.'
|
||||
* #4777: py domain: Add ``:async:`` option to :rst:dir:`py:function` directive
|
||||
* py domain: Add ``:async:``, ``:classmethod:`` and ``:staticmethod:`` options
|
||||
to :rst:dir:`py:method` directive
|
||||
* py domain: Add new options to :rst:dir:`py:method` directive
|
||||
|
||||
- ``:async:``
|
||||
- ``:classmethod:``
|
||||
- ``:property:``
|
||||
- ``:staticmethod:``
|
||||
* #6306: html: Add a label to search form for accessability purposes
|
||||
|
||||
Bugs fixed
|
||||
|
@ -229,9 +229,13 @@ The following directives are provided for module and class contents:
|
||||
The ``classmethod`` option and ``staticmethod`` option can be given (with
|
||||
no value) to indicate the method is a class method (or a static method).
|
||||
|
||||
The ``property`` option can be given (with no value) to indicate the method
|
||||
is a property.
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
|
||||
``:async:``, ``:classmethod:`` and ``:staticmethod:`` options added.
|
||||
``:async:``, ``:classmethod:``, ``:property:`` and ``:staticmethod:``
|
||||
options added.
|
||||
|
||||
.. rst:directive:: .. py:staticmethod:: name(parameters)
|
||||
|
||||
|
@ -587,22 +587,28 @@ class PyMethod(PyObject):
|
||||
option_spec.update({
|
||||
'async': directives.flag,
|
||||
'classmethod': directives.flag,
|
||||
'property': directives.flag,
|
||||
'staticmethod': directives.flag,
|
||||
})
|
||||
|
||||
def needs_arglist(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
if 'property' in self.options:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
# type: (str) -> str
|
||||
prefix = []
|
||||
if 'async' in self.options:
|
||||
prefix.append('async')
|
||||
if 'staticmethod' in self.options:
|
||||
prefix.append('static')
|
||||
if 'classmethod' in self.options:
|
||||
prefix.append('classmethod')
|
||||
if 'property' in self.options:
|
||||
prefix.append('property')
|
||||
if 'staticmethod' in self.options:
|
||||
prefix.append('static')
|
||||
|
||||
if prefix:
|
||||
return ' '.join(prefix) + ' '
|
||||
@ -622,10 +628,12 @@ class PyMethod(PyObject):
|
||||
else:
|
||||
return '%s()' % name
|
||||
|
||||
if 'staticmethod' in self.options:
|
||||
return _('%s() (%s static method)') % (methname, clsname)
|
||||
elif 'classmethod' in self.options:
|
||||
if 'classmethod' in self.options:
|
||||
return _('%s() (%s class method)') % (methname, clsname)
|
||||
elif 'property' in self.options:
|
||||
return _('%s() (%s property)') % (methname, clsname)
|
||||
elif 'staticmethod' in self.options:
|
||||
return _('%s() (%s static method)') % (methname, clsname)
|
||||
else:
|
||||
return _('%s() (%s method)') % (methname, clsname)
|
||||
|
||||
|
@ -1420,6 +1420,37 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||
super().add_content(more_content, no_docstring)
|
||||
|
||||
|
||||
class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
|
||||
"""
|
||||
Specialized Documenter subclass for properties.
|
||||
"""
|
||||
objtype = 'property'
|
||||
directivetype = 'method'
|
||||
member_order = 60
|
||||
|
||||
# before AttributeDocumenter
|
||||
priority = AttributeDocumenter.priority + 1
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
# type: (Any, str, bool, Any) -> bool
|
||||
return inspect.isproperty(member) and isinstance(parent, ClassDocumenter)
|
||||
|
||||
def document_members(self, all_members=False):
|
||||
# type: (bool) -> None
|
||||
pass
|
||||
|
||||
def get_real_modname(self):
|
||||
# type: () -> str
|
||||
return self.get_attr(self.parent or self.object, '__module__', None) \
|
||||
or self.modname
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
# type: (str) -> None
|
||||
super().add_directive_header(sig)
|
||||
self.add_line(' :property:', self.get_sourcename())
|
||||
|
||||
|
||||
class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
"""
|
||||
Specialized Documenter subclass for attributes that cannot be imported
|
||||
@ -1511,6 +1542,7 @@ def setup(app):
|
||||
app.add_autodocumenter(DecoratorDocumenter)
|
||||
app.add_autodocumenter(MethodDocumenter)
|
||||
app.add_autodocumenter(AttributeDocumenter)
|
||||
app.add_autodocumenter(PropertyDocumenter)
|
||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||
|
||||
app.add_config_value('autoclass_content', 'class', True)
|
||||
|
@ -40,7 +40,7 @@ from sphinx.util.rst import escape as rst_escape
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, List, Tuple, Type, Union # NOQA
|
||||
from typing import Any, Callable, Dict, List, Set, Tuple, Type, Union # NOQA
|
||||
from sphinx.builders import Builder # NOQA
|
||||
from sphinx.ext.autodoc import Documenter # NOQA
|
||||
|
||||
@ -170,7 +170,12 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
|
||||
template = template_env.get_template('autosummary/base.rst')
|
||||
|
||||
def get_members(obj, typ, include_public=[], imported=True):
|
||||
# type: (Any, str, List[str], bool) -> Tuple[List[str], List[str]]
|
||||
# type: (Any, Union[str, Set[str]], List[str], bool) -> Tuple[List[str], List[str]] # NOQA
|
||||
if isinstance(typ, str):
|
||||
types = {typ}
|
||||
else:
|
||||
types = typ
|
||||
|
||||
items = [] # type: List[str]
|
||||
for name in dir(obj):
|
||||
try:
|
||||
@ -178,7 +183,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
|
||||
except AttributeError:
|
||||
continue
|
||||
documenter = get_documenter(app, value, obj)
|
||||
if documenter.objtype == typ:
|
||||
if documenter.objtype in types:
|
||||
if imported or getattr(value, '__module__', None) == obj.__name__:
|
||||
# skip imported members if expected
|
||||
items.append(name)
|
||||
@ -203,7 +208,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
|
||||
ns['methods'], ns['all_methods'] = \
|
||||
get_members(obj, 'method', ['__init__'])
|
||||
ns['attributes'], ns['all_attributes'] = \
|
||||
get_members(obj, 'attribute')
|
||||
get_members(obj, {'attribute', 'property'})
|
||||
|
||||
parts = name.split('.')
|
||||
if doc.objtype in ('method', 'attribute'):
|
||||
|
@ -224,6 +224,12 @@ def iscoroutinefunction(obj):
|
||||
return False
|
||||
|
||||
|
||||
def isproperty(obj):
|
||||
# type: (Any) -> bool
|
||||
"""Check if the object is property."""
|
||||
return isinstance(obj, property)
|
||||
|
||||
|
||||
def safe_getattr(obj, name, *defargs):
|
||||
# type: (Any, str, str) -> object
|
||||
"""A getattr() that turns all exceptions into AttributeErrors."""
|
||||
|
@ -761,7 +761,7 @@ def test_autodoc_undoc_members(app):
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
' .. py:method:: Class.meth()',
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
|
||||
' .. py:attribute:: Class.skipattr',
|
||||
' .. py:method:: Class.skipmeth()',
|
||||
@ -782,6 +782,7 @@ def test_autodoc_inherited_members(app):
|
||||
' .. py:method:: Class.inheritedstaticmeth(cls)',
|
||||
' .. py:method:: Class.meth()',
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:method:: Class.skipmeth()'
|
||||
]
|
||||
|
||||
@ -841,7 +842,7 @@ def test_autodoc_special_members(app):
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
' .. py:method:: Class.meth()',
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
|
||||
' .. py:attribute:: Class.skipattr',
|
||||
' .. py:method:: Class.skipmeth()',
|
||||
@ -1033,7 +1034,7 @@ def test_autodoc_member_order(app):
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.skipattr',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:attribute:: Class.udocattr',
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
@ -1067,7 +1068,7 @@ def test_autodoc_member_order(app):
|
||||
' .. py:attribute:: Class.inst_attr_inline',
|
||||
' .. py:attribute:: Class.inst_attr_string',
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:attribute:: Class.skipattr',
|
||||
' .. py:attribute:: Class.udocattr'
|
||||
]
|
||||
@ -1090,7 +1091,7 @@ def test_autodoc_member_order(app):
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
' .. py:method:: Class.meth()',
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
|
||||
' .. py:attribute:: Class.skipattr',
|
||||
' .. py:method:: Class.skipmeth()',
|
||||
@ -1157,14 +1158,16 @@ def test_autodoc_docstring_signature(app):
|
||||
' indented line',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: DocstringSig.prop1',
|
||||
' .. py:method:: DocstringSig.prop1',
|
||||
' :module: target',
|
||||
' :property:',
|
||||
' ',
|
||||
' First line of docstring',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: DocstringSig.prop2',
|
||||
' .. py:method:: DocstringSig.prop2',
|
||||
' :module: target',
|
||||
' :property:',
|
||||
' ',
|
||||
' First line of docstring',
|
||||
' Second line of docstring',
|
||||
@ -1199,15 +1202,17 @@ def test_autodoc_docstring_signature(app):
|
||||
' indented line',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: DocstringSig.prop1',
|
||||
' .. py:method:: DocstringSig.prop1',
|
||||
' :module: target',
|
||||
' :property:',
|
||||
' ',
|
||||
' DocstringSig.prop1(self)',
|
||||
' First line of docstring',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: DocstringSig.prop2',
|
||||
' .. py:method:: DocstringSig.prop2',
|
||||
' :module: target',
|
||||
' :property:',
|
||||
' ',
|
||||
' First line of docstring',
|
||||
' Second line of docstring',
|
||||
@ -1722,7 +1727,7 @@ def test_autodoc_default_options_with_values(app):
|
||||
' .. py:method:: Class.skipmeth()',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:attribute:: Class.prop',
|
||||
' .. py:method:: Class.prop',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:attribute:: Class.udocattr',
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
|
@ -333,7 +333,9 @@ def test_pymethod_options(app):
|
||||
" .. py:method:: meth3\n"
|
||||
" :staticmethod:\n"
|
||||
" .. py:method:: meth4\n"
|
||||
" :async:\n")
|
||||
" :async:\n"
|
||||
" .. py:method:: meth5\n"
|
||||
" :property:\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
@ -346,6 +348,8 @@ def test_pymethod_options(app):
|
||||
addnodes.index,
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc)])]))
|
||||
|
||||
# method
|
||||
@ -387,6 +391,15 @@ def test_pymethod_options(app):
|
||||
assert 'Class.meth4' in domain.objects
|
||||
assert domain.objects['Class.meth4'] == ('index', 'method')
|
||||
|
||||
# :property:
|
||||
assert_node(doctree[1][1][8], addnodes.index,
|
||||
entries=[('single', 'meth5() (Class property)', 'Class.meth5', '', None)])
|
||||
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
|
||||
[desc_name, "meth5"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth5' in domain.objects
|
||||
assert domain.objects['Class.meth5'] == ('index', 'method')
|
||||
|
||||
|
||||
def test_pyclassmethod(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
|
@ -475,3 +475,14 @@ def test_isattributedescriptor(app):
|
||||
assert inspect.isattributedescriptor(types.FrameType.f_locals) is True # GetSetDescriptorType # NOQA
|
||||
assert inspect.isattributedescriptor(datetime.timedelta.days) is True # MemberDescriptorType # NOQA
|
||||
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
|
||||
|
||||
|
||||
def test_isproperty(app):
|
||||
from target.functions import func
|
||||
from target.methods import Base
|
||||
|
||||
assert inspect.isproperty(Base.prop) is True # property of class
|
||||
assert inspect.isproperty(Base().prop) is False # property of instance
|
||||
assert inspect.isproperty(Base.meth) is False # method of class
|
||||
assert inspect.isproperty(Base().meth) is False # method of instance
|
||||
assert inspect.isproperty(func) is False # function
|
||||
|
Loading…
Reference in New Issue
Block a user