Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1015.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sphinx inline types rejected with an ellipsis or quotes
17 changes: 10 additions & 7 deletions docsig/_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@
_NO_RETURN = ("NoReturn", "Never")

#: a word in a param field after the leading keyword: a (possibly
#: starred) name or type expression, in which ``. , | [ ]`` join word
#: characters so types such as ``list[str]``, ``t.Any``, ``int|str``,
#: and the split words of ``dict[str, int]`` hold together; a trailing
#: ``,`` or bracket belongs to a bracketed type, while a trailing ``.``
#: or ``|`` stays outside the word so it is still read as a bad
#: closing token
_FIELD_WORD = r"(?:\\?\*){0,2}\w+(?:[.,|\[\]]+\w+)*[,\[\]]*"
#: starred) name or type-expression fragment built from word characters
#: and the punctuation that joins them in a type — ``. , | [ ] ' "`` —
#: so ``list[str]``, ``t.Any``, ``int|str``, the split words of
#: ``dict[str, int]``, an ellipsis such as ``tuple[int, ...]`` or
#: ``Callable[..., str]``, and quoted members such as ``Literal['a']``
#: or ``Annotated[int, "m"]`` all hold together; the word may not *end*
#: on a ``.`` or ``|``, so a stray trailing one before whitespace is
#: left outside the word and read as a bad closing token, while a ``,``
#: or bracket still belongs to a bracketed type
_FIELD_WORD = r"(?:\\?\*){0,2}[\w.,|\[\]'\"]*[\w,\[\]'\"]"

#: a ``..`` directive and its indented block, which may be indented
#: arbitrarily and so never counts as a param indent anomaly
Expand Down
38 changes: 38 additions & 0 deletions tests/fix_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2937,3 +2937,41 @@ def function(param1, param2) -> int:
)
== 0
)


def test_fix_sphinx_inline_type_with_ellipsis_or_quoted_member(
capsys: pytest.CaptureFixture,
init_file: FixtureInitFile,
main: FixtureMain,
) -> None:
"""Parse a sphinx type-before-name field with ``...`` or quotes.

Problem: the ``:param <type> <name>:`` field grammar only joined
word characters with ``. , | [ ]``, so a bare ellipsis such as
``tuple[int, ...]`` or ``Callable[..., str]`` and quoted members
such as ``Literal['a', 'b']`` terminated the type early. The
parameter name became a fragment of the type (``tuple[int``,
``Callable[``) and the next character was read as a bad closing
token, spuriously emitting SIG302/SIG304/SIG404 on legal code.

:param capsys: Capture sys out.
:param init_file: Initialize a test file.
:param main: Mock ``main`` function.
"""
template = '''
def function(cb, items, mode) -> int:
"""Summary.

:param Callable[..., str] cb: A callback.
:param tuple[int, ...] items: The numbers.
:param Literal['a', 'b'] mode: The mode.
:return: A number.
"""
return 1
'''
init_file(template)
assert main(".") == 0
std = capsys.readouterr()
assert E[302].ref not in std.out
assert E[304].ref not in std.out
assert E[404].ref not in std.out
Loading