Skip to content

Commit

Permalink
Merge branch 'master' into use-anchor-for-search-previews
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz committed Feb 24, 2024
2 parents 7938764 + 707bfbd commit 00607a9
Show file tree
Hide file tree
Showing 23 changed files with 395 additions and 97 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Features added
* #11892: Improved performance when resolving cross references in cpp domain.
Patch by Rouslan Korneychuk.

* #11981: Improve rendering of signatures using ``slice`` syntax,
e.g., ``def foo(arg: np.float64[:,:]) -> None: ...``.

Bugs fixed
----------

Expand Down Expand Up @@ -77,6 +80,8 @@ Bugs fixed
* #11925: Blacklist the ``sphinxprettysearchresults`` extension; the functionality
it provides was merged into Sphinx v2.0.0.
Patch by James Addison.
* #11962: Fix target resolution when using ``:paramtype:`` fields.
Patch by Bénédikt Tran.

Testing
-------
Expand Down
4 changes: 2 additions & 2 deletions EXAMPLES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Documentation using the classic theme
* `EZ-Draw <https://pageperso.lis-lab.fr/~edouard.thiel/ez-draw/doc/en/html/ez-manual.html>`__ (customized)
* `Generic Mapping Tools (GMT) <https://gmt.soest.hawaii.edu/doc/latest/>`__ (customized)
* `Genomedata <https://noble.gs.washington.edu/proj/genomedata/doc/1.3.3/>`__
* `GetFEM++ <https://getfem.org/>`__ (customized)
* `GetFEM <https://getfem.org/>`__ (customized)
* `Glasgow Haskell Compiler <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/>`__ (customized)
* `Grok <https://web.archive.org/web/20230708190705/http://grok.zope.org/doc/current/>`__ (customized)
* `GROMACS <https://manual.gromacs.org/documentation/>`__
Expand Down Expand Up @@ -132,7 +132,6 @@ Documentation using the nature theme
* `libLAS <https://liblas.org/>`__ (customized)
* `Lmod <https://lmod.readthedocs.io/>`__
* `MapServer <https://mapserver.org/>`__ (customized)
* `pyglet <https://pyglet.readthedocs.io/>`__ (customized)
* `PyWavelets <https://pywavelets.readthedocs.io/>`__
* `Setuptools <https://setuptools.readthedocs.io/>`__
* `Spring Python <https://docs.spring.io/spring-python/1.2.x/sphinx/html/>`__
Expand Down Expand Up @@ -253,6 +252,7 @@ Documentation using sphinx_rtd_theme
* `PROS <https://pros.cs.purdue.edu/v5/>`__ (customized)
* `Pweave <https://mpastell.com/pweave/>`__
* `pyca/cryptograhpy <https://cryptography.io/>`__
* `pyglet <https://pyglet.readthedocs.io/>`__
* `PyNaCl <https://pynacl.readthedocs.io/>`__
* `pyOpenSSL <https://www.pyopenssl.org/>`__
* `PyPy <https://doc.pypy.org/>`__
Expand Down
7 changes: 5 additions & 2 deletions doc/usage/restructuredtext/roles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,10 @@ different style:
:rst:role:`code` role instead.

.. versionchanged:: 1.8
Allowed to escape curly braces with backslash
Allowed to escape curly braces with double backslash. For example, in
``:samp:`print(f"answer=\\{1+{variable}*2\\}")```, the part ``variable``
would be emphasized and the escaped curly braces would be displayed:
:samp:`print(f"answer=\\{1+{variable}*2\\}")`

There is also an :rst:role:`index` role to generate index entries.

Expand Down Expand Up @@ -273,7 +276,7 @@ the standard reST markup for that purpose.
Substitutions
-------------

The documentation system provides three substitutions that are defined by
The documentation system provides some substitutions that are defined by
default. They are set in the build configuration file.

.. describe:: |release|
Expand Down
4 changes: 1 addition & 3 deletions sphinx/environment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@

if TYPE_CHECKING:
from collections.abc import MutableMapping
from typing import Literal

from typing_extensions import overload
from typing import Literal, overload

from sphinx.domains.c import CDomain
from sphinx.domains.changeset import ChangeSetDomain
Expand Down
2 changes: 1 addition & 1 deletion sphinx/ext/napoleon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def _patch_python_domain() -> None:
PyObject.doc_field_types.append(
PyTypedField('keyword', label=_('Keyword Arguments'),
names=('keyword', 'kwarg', 'kwparam'),
typerolename='obj', typenames=('paramtype', 'kwtype'),
typerolename='class', typenames=('paramtype', 'kwtype'),
can_collapse=True))


Expand Down
14 changes: 14 additions & 0 deletions sphinx/pycode/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,20 @@ def visit_Name(self, node: ast.Name) -> str:
def visit_Set(self, node: ast.Set) -> str:
return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"

def visit_Slice(self, node: ast.Slice) -> str:
if not node.lower and not node.upper and not node.step:
# Empty slice with default values -> [:]
return ":"

start = self.visit(node.lower) if node.lower else ""
stop = self.visit(node.upper) if node.upper else ""
if not node.step:
# Default step size -> [start:stop]
return f"{start}:{stop}"

step = self.visit(node.step) if node.step else ""
return f"{start}:{stop}:{step}"

def visit_Subscript(self, node: ast.Subscript) -> str:
def is_simple_tuple(value: ast.expr) -> bool:
return (
Expand Down
2 changes: 1 addition & 1 deletion sphinx/search/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def get_terms(self, fn2index: dict[str, int]) -> tuple[dict[str, list[int] | int
if fn in fn2index:
rv[k] = fn2index[fn]
else:
rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index])
rv[k] = sorted(fn2index[fn] for fn in v if fn in fn2index)
return rvs

def freeze(self) -> dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion sphinx/util/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
if id(obj) in seen:
return 'tuple(...)'
seen |= frozenset([id(obj)])
return '(%s%s)' % (
return '({}{})'.format(
', '.join(object_description(x, _seen=seen) for x in obj),
',' * (len(obj) == 1),
)
Expand Down
4 changes: 2 additions & 2 deletions sphinx/writers/html5.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ def depart_desc_type_parameter(self, node: Element) -> None:
self.depart_desc_parameter(node)

def visit_desc_optional(self, node: Element) -> None:
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.params_left_at_level = sum(isinstance(c, addnodes.desc_parameter)
for c in node.children)
self.optional_param_level += 1
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
Expand Down
4 changes: 2 additions & 2 deletions sphinx/writers/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,8 +911,8 @@ def depart_desc_type_parameter(self, node: Element) -> None:
self._depart_sig_parameter(node)

def visit_desc_optional(self, node: Element) -> None:
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.params_left_at_level = sum(isinstance(c, addnodes.desc_parameter)
for c in node.children)
self.optional_param_level += 1
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
Expand Down
4 changes: 2 additions & 2 deletions sphinx/writers/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,8 @@ def visit_desc_type_parameter(self, node: Element) -> None:
self.visit_desc_parameter(node)

def visit_desc_optional(self, node: Element) -> None:
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.params_left_at_level = sum(isinstance(c, addnodes.desc_parameter)
for c in node.children)
self.optional_param_level += 1
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
Expand Down
3 changes: 3 additions & 0 deletions tests/roots/test-ext-autodoc/target/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ async def asyncgenerator():

builtin_func = print
partial_builtin_func = partial(print)

def slice_arg_func(arg: 'float64[:, :]'):
pass
15 changes: 15 additions & 0 deletions tests/roots/test-ext-napoleon-paramtype/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import sys

sys.path.insert(0, os.path.abspath('.'))
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.intersphinx'
]

# Python inventory is manually created in the test
# in order to avoid creating a real HTTP connection
intersphinx_mapping = {}
intersphinx_cache_limit = 0
intersphinx_disabled_reftypes = []
8 changes: 8 additions & 0 deletions tests/roots/test-ext-napoleon-paramtype/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
test-ext-napoleon
=================

.. automodule:: pkg.bar
:members:

.. automodule:: pkg.foo
:members:
Empty file.
10 changes: 10 additions & 0 deletions tests/roots/test-ext-napoleon-paramtype/pkg/bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Bar:
"""The bar."""
def list(self) -> None:
"""A list method."""

@staticmethod
def int() -> float:
"""An int method."""
return 1.0

27 changes: 27 additions & 0 deletions tests/roots/test-ext-napoleon-paramtype/pkg/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Foo:
"""The foo."""
def do(
self,
*,
keyword_paramtype,
keyword_kwtype,
kwarg_paramtype,
kwarg_kwtype,
kwparam_paramtype,
kwparam_kwtype,
):
"""Some method.
:keyword keyword_paramtype: some param
:paramtype keyword_paramtype: list[int]
:keyword keyword_kwtype: some param
:kwtype keyword_kwtype: list[int]
:kwarg kwarg_paramtype: some param
:paramtype kwarg_paramtype: list[int]
:kwarg kwarg_kwtype: some param
:kwtype kwarg_kwtype: list[int]
:kwparam kwparam_paramtype: some param
:paramtype kwparam_paramtype: list[int]
:kwparam kwparam_kwtype: some param
:kwtype kwparam_kwtype: list[int]
"""
12 changes: 7 additions & 5 deletions tests/roots/test-manpage_url/index.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
* :manpage:`man(1)`
* :manpage:`ls.1`
* :manpage:`sphinx`
* :manpage:`mailx(1) <bsd-mailx/mailx.1>`
* :manpage:`!man(1)`
The :manpage:`cp(1)`
--------------------
* :manpage:`man(1)`
* :manpage:`ls.1`
* :manpage:`sphinx`
* :manpage:`mailx(1) <bsd-mailx/mailx.1>`
* :manpage:`!man(1)`
14 changes: 14 additions & 0 deletions tests/test_builders/test_build_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@ def test_html_sidebar(app, status, warning):
assert ctx['sidebars'] == []


@pytest.mark.parametrize(("fname", "expect"), [
('index.html', (".//h1/em/a[@href='https://example.com/cp.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/man.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/ls.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/sphinx.']", '', True)),
])
@pytest.mark.sphinx('html', testroot='manpage_url', confoverrides={
'manpages_url': 'https://example.com/{page}.{section}'})
@pytest.mark.test_params(shared_result='test_build_html_manpage_url')
def test_html_manpage(app, cached_etree_parse, fname, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)


@pytest.mark.sphinx('html', testroot='toctree-glob',
confoverrides={'html_baseurl': 'https://example.com/'})
def test_html_baseurl(app, status, warning):
Expand Down
11 changes: 11 additions & 0 deletions tests/test_extensions/test_ext_autodoc_autofunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,14 @@ def test_async_generator(app):
' :async:',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_slice_function_arg(app):
actual = do_autodoc(app, 'function', 'target.functions.slice_arg_func')
assert list(actual) == [
'',
'.. py:function:: slice_arg_func(arg: float64[:, :])',
' :module: target.functions',
'',
]
43 changes: 43 additions & 0 deletions tests/test_extensions/test_ext_napoleon_docstring.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"""Tests for :mod:`sphinx.ext.napoleon.docstring` module."""

import re
import zlib
from collections import namedtuple
from inspect import cleandoc
from itertools import product
from textwrap import dedent
from unittest import mock

import pytest
from html5lib import HTMLParser

from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.ext.napoleon import Config
from sphinx.ext.napoleon.docstring import (
GoogleDocstring,
Expand Down Expand Up @@ -2659,3 +2663,42 @@ def test_napoleon_and_autodoc_typehints_description_documented_params(app, statu
'\n'
' * ****kwargs** (*int*) -- Extra arguments.\n'
)


@pytest.mark.sphinx('html', testroot='ext-napoleon-paramtype', freshenv=True)
def test_napoleon_keyword_and_paramtype(app, tmp_path):
inv_file = tmp_path / 'objects.inv'
inv_file.write_bytes(b'''\
# Sphinx inventory version 2
# Project: Intersphinx Test
# Version: 42
# The remainder of this file is compressed using zlib.
''' + zlib.compress(b'''\
None py:data 1 none.html -
list py:class 1 list.html -
int py:class 1 int.html -
''')) # NoQA: W291
app.config.intersphinx_mapping = {'python': ('127.0.0.1:5555', str(inv_file))}
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)

app.build(force_all=True)

buffer = (app.outdir / 'index.html').read_bytes()
etree = HTMLParser(namespaceHTMLElements=False).parse(buffer)

for name, typename in product(('keyword', 'kwarg', 'kwparam'), ('paramtype', 'kwtype')):
param = f'{name}_{typename}'
li_ = list(etree.findall(f'.//li/p/strong[.="{param}"]/../..'))
assert len(li_) == 1
li = li_[0]

text = li.text or ''.join(li.itertext())
assert text == f'{param} (list[int]) \u2013 some param'

a_ = list(li.findall('.//a[@class="reference external"]'))

assert len(a_) == 2
for a, uri in zip(a_, ('list.html', 'int.html')):
assert a.attrib['href'] == f'127.0.0.1:5555/{uri}'
assert a.attrib['title'] == '(in Intersphinx Test v42)'

0 comments on commit 00607a9

Please sign in to comment.