Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no_index_entry config variable, and implement :no-index-entry: for autodoc :) #12233

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ Features added
to ``Added in [...]``.
Patch by Bénédikt Tran.

* Added :confval:`no_index_entry` configuration option, and allow autodoc
directives to use ``:no-index-entry:``.
Patch by Jonny Saunders


Bugs fixed
----------
Expand Down
5 changes: 5 additions & 0 deletions doc/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@ General configuration
.. versionchanged:: 6.2
Changed allowable container types to a set, list, or tuple

.. confval:: no_index_entry

If ``True``, set the ``:no-index-entry:`` flag by default on all documented domain
objects (see :doc:`domain </usage/domains/index>` ). Default: ``False`` .

.. confval:: numfig

If true, figures, tables and code-blocks are automatically numbered if they
Expand Down
7 changes: 7 additions & 0 deletions doc/usage/extensions/autodoc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,

.. versionadded:: 0.4

* All autodoc directives support the ``no-index-entry`` flag option that has the
same effect as for standard :rst:dir:`py:function` etc. directives:
cross-references will still be generated, but entries won't be added to
:ref:`genindex`

.. versionadded:: 7.3

* :rst:dir:`automodule` also recognizes the ``synopsis``, ``platform`` and
``deprecated`` options that the standard :rst:dir:`py:module` directive
supports.
Expand Down
1 change: 1 addition & 0 deletions sphinx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class Config:
'smartquotes_excludes': _Opt(
{'languages': ['ja'], 'builders': ['man', 'text']}, 'env', ()),
'option_emphasise_placeholders': _Opt(False, 'env', ()),
'no_index_entry': _Opt(False, 'env', ()),
}

def __init__(self, config: dict[str, Any] | None = None,
Expand Down
3 changes: 2 additions & 1 deletion sphinx/directives/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ def run(self) -> list[Node]:
'no-index-entry' in self.options
# xref RemovedInSphinx90Warning
# deprecate noindexentry in Sphinx 9.0
or 'noindexentry' in self.options)
or 'noindexentry' in self.options
or self.config.no_index_entry)
node['no-contents-entry'] = node['nocontentsentry'] = (
'no-contents-entry' in self.options
# xref RemovedInSphinx90Warning
Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def add_target_and_index(self, ast: ASTDeclaration, sig: str,

self.state.document.note_explicit_target(signode)

if 'no-index-entry' not in self.options:
if not ('no-index-entry' in self.options or self.config.no_index_entry):
indexText = self.get_index_text(name)
self.indexnode['entries'].append(('single', indexText, newestId, '', None))

Expand Down
5 changes: 4 additions & 1 deletion sphinx/domains/cpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ def add_target_and_index(self, ast: ASTDeclaration, sig: str,
if decl.objectType == 'concept':
isInConcept = True
break
if not isInConcept and 'no-index-entry' not in self.options:
if not isInConcept and not (
'no-index-entry' in self.options
or self.config.no_index_entry
):
strippedName = name
for prefix in self.env.config.cpp_index_common_prefix:
if name.startswith(prefix):
Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def add_target_and_index(self, name_obj: tuple[str, str], sig: str,
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
domain.note_object(fullname, self.objtype, node_id, location=signode)

if 'no-index-entry' not in self.options:
if not ('no-index-entry' in self.options or self.config.no_index_entry):
indextext = self.get_index_text(mod_name, name_obj) # type: ignore[arg-type]
if indextext:
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
Expand Down
16 changes: 11 additions & 5 deletions sphinx/domains/python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def needs_arglist(self) -> bool:
def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
signode: desc_signature) -> None:
super().add_target_and_index(name_cls, sig, signode)
if 'no-index-entry' not in self.options:
if not ('no-index-entry' in self.options or self.config.no_index_entry):
modname = self.options.get('module', self.env.ref_context.get('py:module'))
node_id = signode['ids'][0]

Expand Down Expand Up @@ -394,13 +394,18 @@ class PyModule(SphinxDirective):
'noindex': directives.flag,
'nocontentsentry': directives.flag,
'deprecated': directives.flag,
'no-index-entry': directives.flag,
'noindexentry': directives.flag,
}

def run(self) -> list[Node]:
domain = cast(PythonDomain, self.env.get_domain('py'))

modname = self.arguments[0].strip()
no_index = 'no-index' in self.options or 'noindex' in self.options
no_index_entry = ('no-index-entry' in self.options
or 'noindexentry' in self.options
or self.config.no_index_entry)
self.env.ref_context['py:module'] = modname

content_node: Element = nodes.section()
Expand All @@ -425,10 +430,11 @@ def run(self) -> list[Node]:

# the platform and synopsis aren't printed; in fact, they are only
# used in the modindex currently
indextext = f'module; {modname}'
inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)])
# The node order is: index node first, then target node.
ret.append(inode)
if not no_index_entry:
indextext = f'module; {modname}'
inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)])
# The node order is: index node first, then target node.
ret.append(inode)
ret.append(target)
ret.extend(content_node.children)
return ret
Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/python/_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
domain.note_object(canonical_name, self.objtype, node_id, aliased=True,
location=signode)

if 'no-index-entry' not in self.options:
if not ('no-index-entry' in self.options or self.config.no_index_entry):
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def add_target_and_index(self, name: str, sig: str, signode: desc_signature) ->
domain = cast(ReSTDomain, self.env.get_domain('rst'))
domain.note_object(self.objtype, name, node_id, location=signode)

if 'no-index-entry' not in self.options:
if not ('no-index-entry' in self.options or self.config.no_index_entry):
indextext = self.get_index_text(self.objtype, name)
if indextext:
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
Expand Down
16 changes: 16 additions & 0 deletions sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class Documenter:
option_spec: ClassVar[OptionSpec] = {
'no-index': bool_option,
'noindex': bool_option,
'no-index-entry': bool_option,
'noindexentry': bool_option,
}

def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any:
Expand Down Expand Up @@ -544,6 +546,10 @@ def add_directive_header(self, sig: str) -> None:

if self.options.no_index or self.options.noindex:
self.add_line(' :no-index:', sourcename)
if self.options.no_index_entry or self.config.no_index_entry:
self.add_line(' :no-index-entry:', sourcename)
if self.options.noindexentry:
self.add_line(' :noindexentry:', sourcename)
if self.objpath:
# Be explicit about the module, this is necessary since .. class::
# etc. don't support a prepended module name
Expand Down Expand Up @@ -990,6 +996,8 @@ class ModuleDocumenter(Documenter):
'imported-members': bool_option, 'ignore-module-all': bool_option,
'no-value': bool_option,
'noindex': bool_option,
'no-index-entry': bool_option,
'noindexentry': bool_option,
}

def __init__(self, *args: Any) -> None:
Expand Down Expand Up @@ -1054,6 +1062,12 @@ def add_directive_header(self, sig: str) -> None:
self.add_line(' :platform: ' + self.options.platform, sourcename)
if self.options.deprecated:
self.add_line(' :deprecated:', sourcename)
if (
self.options.no_index_entry
or self.options.noindexentry
or self.config.no_index_entry
):
self.add_line(' :no-index-entry:', sourcename)

def get_module_members(self) -> dict[str, ObjectMember]:
"""Get members of target module."""
Expand Down Expand Up @@ -1474,6 +1488,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
'private-members': members_option, 'special-members': members_option,
'class-doc-from': class_doc_from_option,
'noindex': bool_option,
'no-index-entry': bool_option,
'noindexentry': bool_option,
}

# Must be higher than FunctionDocumenter, ClassDocumenter, and
Expand Down
2 changes: 1 addition & 1 deletion sphinx/ext/autodoc/directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
'show-inheritance', 'private-members', 'special-members',
'ignore-module-all', 'exclude-members', 'member-order',
'imported-members', 'class-doc-from', 'no-value']
'imported-members', 'class-doc-from', 'no-value', 'no-index-entry']

AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members',
'exclude-members']
Expand Down
7 changes: 7 additions & 0 deletions tests/test_domains/test_domain_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,13 @@ def test_no_index_entry(app):
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (built-in class)', 'f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])

text = (".. py:module:: h\n"
".. py:module:: i\n"
" :no-index-entry:\n")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, nodes.target, nodes.target))
assert_node(doctree[0], addnodes.index, entries=[('pair', 'module; h', 'module-h', '', None)])


@pytest.mark.sphinx('html', testroot='domain-py-python_use_unqualified_type_names')
def test_python_python_use_unqualified_type_names(app, status, warning):
Expand Down
29 changes: 29 additions & 0 deletions tests/test_extensions/test_ext_autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2921,3 +2921,32 @@ def function_rst(name, sig):
*function_rst('bar', 'x: typing.Literal[1234]'),
*function_rst('foo', 'x: typing.Literal[target.literal.MyEnum.a]'),
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_no_index_entry(app):
# modules can use no-index-entry
options = {'no-index-entry': None}
actual = do_autodoc(app, 'module', 'target.module', options)
assert ' :no-index-entry:' in list(actual)

# classes can use no-index-entry
actual = do_autodoc(app, 'class', 'target.classes.Foo', options)
assert ' :no-index-entry:' in list(actual)

# functions can use no-index-entry
actual = do_autodoc(app, 'function', 'target.functions.func', options)
assert ' :no-index-entry:' in list(actual)

# modules respect config-level no-index-entry
app.config.no_index_entry = True
actual = do_autodoc(app, 'module', 'target.module')
assert ' :no-index-entry:' in list(actual)

# classes respect config-level no-index-entry
actual = do_autodoc(app, 'class', 'target.classes.Foo')
assert ' :no-index-entry:' in list(actual)

# functions respect config-level no-index-entry
actual = do_autodoc(app, 'function', 'target.functions.func')
assert ' :no-index-entry:' in list(actual)