diff --git a/CHANGES b/CHANGES index c1c934591..d2e086922 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,9 @@ Deprecated * ``sphinx.ext.autodoc.importer.MockLoader`` * ``sphinx.ext.autodoc.importer.mock()`` * ``sphinx.ext.autosummary.autolink_role()`` +* ``sphinx.ext.imgmath.DOC_BODY`` +* ``sphinx.ext.imgmath.DOC_BODY_PREVIEW`` +* ``sphinx.ext.imgmath.DOC_HEAD`` * ``sphinx.transforms.CitationReferences`` * ``sphinx.transforms.SmartQuotesSkipper`` * ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()`` @@ -94,9 +97,13 @@ Features added variables (refs: #6232, #6303) * #6287: autodoc: Unable to document bound instance methods exported as module functions +* #6289: autodoc: :confval:`autodoc_default_options` now supports + ``imported-members`` option * #6212 autosummary: Add :confval:`autosummary_imported_members` to display imported members on autosummary * #6271: ``make clean`` is catastrophically broken if building into '.' +* Add ``:classmethod:`` and ``:staticmethod:`` options to :rst:dir:`py:method` + directive Bugs fixed ---------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 97eb0689a..52fdf9f7a 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -177,6 +177,21 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.ext.autosummary.AutoLink`` + * - ``sphinx.ext.imgmath.DOC_BODY`` + - 2.1 + - 4.0 + - N/A + + * - ``sphinx.ext.imgmath.DOC_BODY_PREVIEW`` + - 2.1 + - 4.0 + - N/A + + * - ``sphinx.ext.imgmath.DOC_HEAD`` + - 2.1 + - 4.0 + - N/A + * - ``sphinx.transforms.CitationReferences`` - 2.1 - 4.0 diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 6d7ba8272..0b6061e78 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -387,14 +387,17 @@ There are also config values that you can set: The supported options are ``'members'``, ``'member-order'``, ``'undoc-members'``, ``'private-members'``, ``'special-members'``, - ``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'`` and - ``'exclude-members'``. + ``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``, + ``'imported-members'`` and ``'exclude-members'``. .. versionadded:: 1.8 .. versionchanged:: 2.0 Accepts ``True`` as a value. + .. versionchanged:: 2.1 + Added ``'imported-members'``. + .. confval:: autodoc_docstring_signature Functions imported from C modules cannot be introspected, and therefore the diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index d0da75d4d..10dc93a07 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -216,6 +216,13 @@ The following directives are provided for module and class contents: described for ``function``. See also :ref:`signatures` and :ref:`info-field-lists`. + The ``classmethod`` option and ``staticmethod`` option can be given (with + no value) to indicate the method is a class method (or a static method). + + .. versionchanged:: 2.1 + + ``:classmethod:`` and ``:staticmethod:`` options added. + .. rst:directive:: .. py:staticmethod:: name(parameters) Like :rst:dir:`py:method`, but indicates that the method is a static method. diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index a286b705e..64786e1eb 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -562,10 +562,25 @@ class PyClassmember(PyObject): class PyMethod(PyObject): """Description of a method.""" + option_spec = PyObject.option_spec.copy() + option_spec.update({ + 'classmethod': directives.flag, + 'staticmethod': directives.flag, + }) + def needs_arglist(self): # type: () -> bool return True + def get_signature_prefix(self, sig): + # type: (str) -> str + if 'staticmethod' in self.options: + return 'static ' + elif 'classmethod' in self.options: + return 'classmethod ' + else: + return '' + def get_index_text(self, modname, name_cls): # type: (str, Tuple[str, str]) -> str name, cls = name_cls @@ -579,53 +594,38 @@ class PyMethod(PyObject): else: return '%s()' % name - return _('%s() (%s method)') % (methname, clsname) + if 'staticmethod' in self.options: + return _('%s() (%s static method)') % (methname, clsname) + elif 'classmethod' in self.options: + return _('%s() (%s class method)') % (methname, clsname) + else: + return _('%s() (%s method)') % (methname, clsname) class PyClassMethod(PyMethod): """Description of a classmethod.""" - def get_signature_prefix(self, sig): - # type: (str) -> str - return 'classmethod ' + option_spec = PyObject.option_spec.copy() - def get_index_text(self, modname, name_cls): - # type: (str, Tuple[str, str]) -> str - name, cls = name_cls - try: - clsname, methname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: - clsname = '.'.join([modname, clsname]) - except ValueError: - if modname: - return _('%s() (in module %s)') % (name, modname) - else: - return '%s()' % name + def run(self): + # type: () -> List[nodes.Node] + self.name = 'py:method' + self.options['classmethod'] = True - return _('%s() (%s class method)') % (methname, clsname) + return super().run() class PyStaticMethod(PyMethod): """Description of a staticmethod.""" - def get_signature_prefix(self, sig): - # type: (str) -> str - return 'static ' + option_spec = PyObject.option_spec.copy() - def get_index_text(self, modname, name_cls): - # type: (str, Tuple[str, str]) -> str - name, cls = name_cls - try: - clsname, methname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: - clsname = '.'.join([modname, clsname]) - except ValueError: - if modname: - return _('%s() (in module %s)') % (name, modname) - else: - return '%s()' % name + def run(self): + # type: () -> List[nodes.Node] + self.name = 'py:method' + self.options['staticmethod'] = True - return _('%s() (%s static method)') % (methname, clsname) + return super().run() class PyAttribute(PyObject): diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 4e919d095..bccad0628 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -57,10 +57,6 @@ class ReSTMarkup(ObjectDescription): def get_index_text(self, objectname, name): # type: (str, str) -> str - if self.objtype == 'directive': - return _('%s (directive)') % name - elif self.objtype == 'role': - return _('%s (role)') % name return '' @@ -98,6 +94,10 @@ class ReSTDirective(ReSTMarkup): signode += addnodes.desc_addname(args, args) return name + def get_index_text(self, objectname, name): + # type: (str, str) -> str + return _('%s (directive)') % name + class ReSTRole(ReSTMarkup): """ @@ -108,6 +108,10 @@ class ReSTRole(ReSTMarkup): signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig) return sig + def get_index_text(self, objectname, name): + # type: (str, str) -> str + return _('%s (role)') % name + class ReSTDomain(Domain): """ReStructuredText domain.""" diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index aa2f2276f..ee6ed842b 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1269,6 +1269,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' + directivetype = 'method' member_order = 50 priority = 1 # must be more than FunctionDocumenter @@ -1289,16 +1290,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: if obj is None: obj = self.object - if inspect.isclassmethod(obj): - self.directivetype = 'classmethod' + if (inspect.isclassmethod(obj) or + inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name)): # document class and static members before ordinary ones self.member_order = self.member_order - 1 - elif inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): - self.directivetype = 'staticmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' + return ret def format_args(self): @@ -1314,6 +1310,17 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: args = args.replace('\\', '\\\\') return args + def add_directive_header(self, sig): + # type: (str) -> None + super().add_directive_header(sig) + + sourcename = self.get_sourcename() + obj = self.parent.__dict__.get(self.object_name, self.object) + if inspect.isclassmethod(obj): + self.add_line(' :classmethod:', sourcename) + elif inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): + self.add_line(' :staticmethod:', sourcename) + def document_members(self, all_members=False): # type: (bool) -> None pass diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 8b41d7fe1..42415433b 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -30,7 +30,8 @@ logger = logging.getLogger(__name__) # common option names for autodoc directives AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', 'show-inheritance', 'private-members', 'special-members', - 'ignore-module-all', 'exclude-members', 'member-order'] + 'ignore-module-all', 'exclude-members', 'member-order', + 'imported-members'] class DummyOptionSpec(dict): diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index eb0d35c25..c71db8b12 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -21,12 +21,15 @@ from subprocess import CalledProcessError, PIPE from docutils import nodes import sphinx +from sphinx import package_dir +from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.errors import SphinxError from sphinx.locale import _, __ from sphinx.util import logging from sphinx.util.math import get_node_equation_number, wrap_displaymath from sphinx.util.osutil import ensuredir from sphinx.util.png import read_png_depth, write_png_depth +from sphinx.util.template import LaTeXRenderer if False: # For type annotation @@ -38,6 +41,8 @@ if False: logger = logging.getLogger(__name__) +templates_path = path.join(package_dir, 'templates', 'imgmath') + class MathExtError(SphinxError): category = 'Math extension error' @@ -87,19 +92,27 @@ DOC_BODY_PREVIEW = r''' depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]') -def generate_latex_macro(math, config): - # type: (str, Config) -> str +def generate_latex_macro(math, config, confdir=''): + # type: (str, Config, str) -> str """Generate LaTeX macro.""" - fontsize = config.imgmath_font_size - baselineskip = int(round(fontsize * 1.2)) + variables = { + 'fontsize': config.imgmath_font_size, + 'baselineskip': int(round(config.imgmath_font_size * 1.2)), + 'preamble': config.imgmath_latex_preamble, + 'math': math + } - latex = DOC_HEAD + config.imgmath_latex_preamble if config.imgmath_use_preview: - latex += DOC_BODY_PREVIEW % (fontsize, baselineskip, math) + template_name = 'preview.tex_t' else: - latex += DOC_BODY % (fontsize, baselineskip, math) + template_name = 'template.tex_t' - return latex + for template_dir in config.templates_path: + template = path.join(confdir, template_dir, template_name) + if path.exists(template): + return LaTeXRenderer().render(template, variables) + + return LaTeXRenderer(templates_path).render(template_name, variables) def ensure_tempdir(builder): @@ -220,7 +233,7 @@ def render_math(self, math): if image_format not in SUPPORT_FORMAT: raise MathExtError('imgmath_image_format must be either "png" or "svg"') - latex = generate_latex_macro(math, self.builder.config) + latex = generate_latex_macro(math, self.builder.config, self.builder.confdir) filename = "%s.%s" % (sha1(latex.encode()).hexdigest(), image_format) relfn = posixpath.join(self.builder.imgpath, 'math', filename) @@ -332,6 +345,15 @@ def html_visit_displaymath(self, node): raise nodes.SkipNode +deprecated_alias('sphinx.ext.imgmath', + { + 'DOC_BODY': DOC_BODY, + 'DOC_BODY_PREVIEW': DOC_BODY_PREVIEW, + 'DOC_HEAD': DOC_HEAD, + }, + RemovedInSphinx40Warning) + + def setup(app): # type: (Sphinx) -> Dict[str, Any] app.add_html_math_renderer('imgmath', diff --git a/sphinx/templates/imgmath/preview.tex_t b/sphinx/templates/imgmath/preview.tex_t new file mode 100644 index 000000000..719dbd5f7 --- /dev/null +++ b/sphinx/templates/imgmath/preview.tex_t @@ -0,0 +1,18 @@ +\documentclass[12pt]{article} +\usepackage[utf8x]{inputenc} +\usepackage{amsmath} +\usepackage{amsthm} +\usepackage{amssymb} +\usepackage{amsfonts} +\usepackage{anyfontsize} +\usepackage{bm} +\pagestyle{empty} +<%= preamble %> + +\usepackage[active]{preview} + +\begin{document} +\begin{preview} +\fontsize{<%= fontsize %>}{<%= baselineskip %}}\selectfont <%= math %> +\end{preview} +\end{document} diff --git a/sphinx/templates/imgmath/template.tex_t b/sphinx/templates/imgmath/template.tex_t new file mode 100644 index 000000000..19834bdc8 --- /dev/null +++ b/sphinx/templates/imgmath/template.tex_t @@ -0,0 +1,14 @@ +\documentclass[12pt]{article} +\usepackage[utf8x]{inputenc} +\usepackage{amsmath} +\usepackage{amsthm} +\usepackage{amssymb} +\usepackage{amsfonts} +\usepackage{anyfontsize} +\usepackage{bm} +\pagestyle{empty} +<%= preamble %> + +\begin{document} +\fontsize{<%= fontsize %>}{<%= baselineskip %>}\selectfont <%= math %> +\end{document} diff --git a/sphinx/util/template.py b/sphinx/util/template.py index 704a42c05..c33e16819 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -67,9 +67,10 @@ class SphinxRenderer(FileRenderer): class LaTeXRenderer(SphinxRenderer): - def __init__(self): - # type: () -> None - template_path = os.path.join(package_dir, 'templates', 'latex') + def __init__(self, template_path=None): + # type: (str) -> None + if template_path is None: + template_path = os.path.join(package_dir, 'templates', 'latex') super().__init__(template_path) # use texescape as escape filter diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index b6b230106..a1deaad7d 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -702,9 +702,9 @@ def test_autodoc_members(app): actual = do_autodoc(app, 'class', 'target.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', - ' .. py:classmethod:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedclassmeth()', ' .. py:method:: Base.inheritedmeth()', - ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ' .. py:method:: Base.inheritedstaticmeth(cls)' ] # default specific-members @@ -713,7 +713,7 @@ def test_autodoc_members(app): assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ' .. py:method:: Base.inheritedmeth()', - ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ' .. py:method:: Base.inheritedstaticmeth(cls)' ] @@ -724,7 +724,7 @@ def test_autodoc_exclude_members(app): actual = do_autodoc(app, 'class', 'target.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', - ' .. py:classmethod:: Base.inheritedclassmeth()' + ' .. py:method:: Base.inheritedclassmeth()' ] # members vs exclude-members @@ -752,9 +752,9 @@ def test_autodoc_undoc_members(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -769,11 +769,11 @@ def test_autodoc_inherited_members(app): actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: 'method::' in l, actual)) == [ ' .. py:method:: Class.excludemeth()', - ' .. py:classmethod:: Class.inheritedclassmeth()', + ' .. py:method:: Class.inheritedclassmeth()', ' .. py:method:: Class.inheritedmeth()', - ' .. py:staticmethod:: Class.inheritedstaticmeth(cls)', + ' .. py:method:: Class.inheritedstaticmeth(cls)', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:method:: Class.skipmeth()' ] @@ -832,9 +832,9 @@ def test_autodoc_special_members(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -952,6 +952,34 @@ def test_autodoc_inner_class(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_classmethod(app): + actual = do_autodoc(app, 'method', 'target.Base.inheritedclassmeth') + assert list(actual) == [ + '', + '.. py:method:: Base.inheritedclassmeth()', + ' :module: target', + ' :classmethod:', + '', + ' Inherited class method.', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_staticmethod(app): + actual = do_autodoc(app, 'method', 'target.Base.inheritedstaticmeth') + assert list(actual) == [ + '', + '.. py:method:: Base.inheritedstaticmeth(cls)', + ' :module: target', + ' :staticmethod:', + '', + ' Inherited static method.', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_descriptor(app): actual = do_autodoc(app, 'attribute', 'target.Class.descr') @@ -1001,8 +1029,8 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string', @@ -1019,8 +1047,8 @@ def test_autodoc_member_order(app): '.. py:class:: Class(arg)', ' .. py:method:: Class.excludemeth()', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class._private_inst_attr', @@ -1053,9 +1081,9 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -1557,6 +1585,8 @@ def test_autodoc_default_options(app): assert ' .. py:attribute:: EnumCls.val4' not in actual actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: target.CustomIter' not in actual + actual = do_autodoc(app, 'module', 'target') + assert '.. py:function:: save_traceback(app)' not in actual # with :members: app.config.autodoc_default_options = {'members': None} @@ -1620,6 +1650,15 @@ def test_autodoc_default_options(app): assert ' .. py:method:: CustomIter.snafucate()' in actual assert ' Makes this snafucated.' in actual + # with :imported-members: + app.config.autodoc_default_options = { + 'members': None, + 'imported-members': None, + 'ignore-module-all': None, + } + actual = do_autodoc(app, 'module', 'target') + assert '.. py:function:: save_traceback(app)' in actual + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options_with_values(app): @@ -1648,7 +1687,7 @@ def test_autodoc_default_options_with_values(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string', diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index e7ff407bd..5a4db3299 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -315,25 +315,54 @@ def test_pyfunction(app): assert domain.objects['func'] == ('index', 'function') -def test_pymethod(app): +def test_pymethod_options(app): text = (".. py:class:: Class\n" "\n" - " .. py:method:: meth\n") + " .. py:method:: meth1\n" + " .. py:method:: meth2\n" + " :classmethod:\n" + " .. py:method:: meth3\n" + " :staticmethod:\n") domain = app.env.get_domain('py') doctree = restructuredtext.parse(app, text) assert_node(doctree, (addnodes.index, [desc, ([desc_signature, ([desc_annotation, "class "], [desc_name, "Class"])], [desc_content, (addnodes.index, + desc, + addnodes.index, + desc, + addnodes.index, desc)])])) + # method assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'meth() (Class method)', 'Class.meth', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth"], + entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"], [desc_parameterlist, ()])], [desc_content, ()])) - assert 'Class.meth' in domain.objects - assert domain.objects['Class.meth'] == ('index', 'method') + assert 'Class.meth1' in domain.objects + assert domain.objects['Class.meth1'] == ('index', 'method') + + # :classmethod: + assert_node(doctree[1][1][2], addnodes.index, + entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)]) + assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "], + [desc_name, "meth2"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth2' in domain.objects + assert domain.objects['Class.meth2'] == ('index', 'method') + + # :staticmethod: + assert_node(doctree[1][1][4], addnodes.index, + entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)]) + assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "], + [desc_name, "meth3"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth3' in domain.objects + assert domain.objects['Class.meth3'] == ('index', 'method') def test_pyclassmethod(app): @@ -354,7 +383,7 @@ def test_pyclassmethod(app): [desc_parameterlist, ()])], [desc_content, ()])) assert 'Class.meth' in domain.objects - assert domain.objects['Class.meth'] == ('index', 'classmethod') + assert domain.objects['Class.meth'] == ('index', 'method') def test_pystaticmethod(app): @@ -375,7 +404,7 @@ def test_pystaticmethod(app): [desc_parameterlist, ()])], [desc_content, ()])) assert 'Class.meth' in domain.objects - assert domain.objects['Class.meth'] == ('index', 'staticmethod') + assert domain.objects['Class.meth'] == ('index', 'method') def test_pyattribute(app):