File: test_init.py

package info (click to toggle)
sphinx-autodoc-typehints 3.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 684 kB
  • sloc: python: 5,081; makefile: 3
file content (180 lines) | stat: -rw-r--r-- 6,474 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
from __future__ import annotations

import inspect
from typing import Any
from unittest.mock import MagicMock, create_autospec, patch

from sphinx.application import Sphinx
from sphinx.config import Config

import sphinx_autodoc_typehints as sat
from sphinx_autodoc_typehints import _inject_overload_signatures, process_docstring, process_signature
from sphinx_autodoc_typehints._resolver._util import get_obj_location
from sphinx_autodoc_typehints.patches import _OVERLOADS_CACHE


class _ClassWithPrivate:
    def __secret(self, x: int) -> str: ...


def _make_sig_app(**overrides: Any) -> Sphinx:
    defaults = {
        "typehints_fully_qualified": False,
        "simplify_optional_unions": False,
        "typehints_formatter": None,
        "typehints_use_signature": False,
        "typehints_use_signature_return": False,
        "autodoc_type_aliases": {},
    }
    defaults.update(overrides)
    config = create_autospec(Config, **defaults)  # ty: ignore[invalid-argument-type]
    return create_autospec(Sphinx, config=config)


def _make_docstring_app(**overrides: Any) -> Sphinx:
    config = MagicMock()
    config.__getitem__ = getattr
    config.autodoc_type_aliases = {}
    config.autodoc_mock_imports = []
    config.typehints_fully_qualified = False
    config.simplify_optional_unions = False
    config.typehints_formatter = None
    config.typehints_document_rtype = True
    config.typehints_document_rtype_none = True
    config.typehints_use_rtype = True
    config.typehints_defaults = None
    config.always_document_param_types = False
    config.python_display_short_literal_types = False
    for key, value in overrides.items():
        setattr(config, key, value)
    app = MagicMock(spec=Sphinx)
    app.config = config
    app.env = None
    return app


def test_process_signature_class_attr_lookup_fails() -> None:
    """Line 111: class name not found on module returns None."""

    def fake_method(self: Any, x: int) -> str: ...

    fake_method.__annotations__ = {"x": int, "return": str}
    fake_method.__qualname__ = "NonExistentClass.method"
    fake_method.__module__ = __name__

    app = _make_sig_app()
    result = process_signature(app, "method", "test.NonExistentClass.method", fake_method, MagicMock(), "", "")
    assert result is None


def test_process_signature_private_name_mangling() -> None:
    """Line 114: dunder-prefixed method names get mangled."""
    method = _ClassWithPrivate.__dict__["_ClassWithPrivate__secret"]

    app = _make_sig_app()
    result = process_signature(app, "method", "test_init._ClassWithPrivate.__secret", method, MagicMock(), "", "")
    assert result is not None
    sig_str, _ = result
    assert "self" not in sig_str


def test_process_docstring_sphinx_signature_raises_value_error() -> None:
    """Lines 157-158: sphinx_signature raising ValueError sets signature to None."""

    def func(x: int) -> str: ...

    app = _make_docstring_app(typehints_document_rtype=False)
    lines: list[str] = [":param x: the x"]
    with patch("sphinx_autodoc_typehints.sphinx_signature", side_effect=ValueError("bad")):
        process_docstring(app, "function", "func", func, None, lines)
    assert ":param x: the x" in lines


def test_process_docstring_sphinx_signature_raises_type_error() -> None:
    """Lines 157-158: sphinx_signature raising TypeError sets signature to None."""

    def func(x: int) -> str: ...

    app = _make_docstring_app(typehints_document_rtype=False)
    lines: list[str] = [":param x: the x"]
    with patch("sphinx_autodoc_typehints.sphinx_signature", side_effect=TypeError("bad")):
        process_docstring(app, "function", "func", func, None, lines)
    assert ":param x: the x" in lines


def test_inject_overload_no_qualname() -> None:
    """Line 198: obj without __qualname__ returns False."""
    obj = MagicMock(spec=[])
    obj.__module__ = "some_module"
    obj.__qualname__ = ""
    _OVERLOADS_CACHE["some_module"] = {}
    try:
        app = _make_docstring_app()
        assert _inject_overload_signatures(app, "function", "name", obj, []) is False
    finally:
        _OVERLOADS_CACHE.pop("some_module", None)


def test_inject_overload_unannotated_param_and_no_return() -> None:
    """Lines 214, 217->224: overload with unannotated param and no return annotation."""
    obj = MagicMock()
    obj.__module__ = "test_mod"
    obj.__qualname__ = "func"

    sig = inspect.Signature(
        parameters=[inspect.Parameter("x", inspect.Parameter.POSITIONAL_OR_KEYWORD)],
    )
    _OVERLOADS_CACHE["test_mod"] = {"func": [sig]}
    try:
        app = _make_docstring_app()
        lines: list[str] = []
        result = _inject_overload_signatures(app, "function", "name", obj, lines)
        assert result is True
        joined = "\n".join(lines)
        assert "**x**" in joined
        assert "\u2192" not in joined
    finally:
        _OVERLOADS_CACHE.pop("test_mod", None)


def test_inject_overload_empty_overloads_returns_false() -> None:
    """Line 233: no matching overloads returns False."""
    obj = MagicMock()
    obj.__module__ = "test_mod2"
    obj.__qualname__ = "func"

    _OVERLOADS_CACHE["test_mod2"] = {"other_func": []}
    try:
        app = _make_docstring_app()
        assert _inject_overload_signatures(app, "function", "name", obj, []) is False
    finally:
        _OVERLOADS_CACHE.pop("test_mod2", None)


def test_local_function_warning_includes_location() -> None:
    def fake_method(self: Any, x: int) -> str: ...

    fake_method.__annotations__ = {"x": int, "return": str}
    fake_method.__qualname__ = "outer.<locals>.inner"
    fake_method.__module__ = __name__

    app = _make_sig_app()
    mock_logger = MagicMock()
    with patch("sphinx_autodoc_typehints._LOGGER", mock_logger):
        process_signature(app, "method", "test.outer.<locals>.inner", fake_method, MagicMock(), "", "")
    mock_logger.warning.assert_called_once()
    kwargs = mock_logger.warning.call_args.kwargs
    assert "location" in kwargs
    assert kwargs["location"] == get_obj_location(fake_method)


def test_inject_types_no_signature() -> None:
    """Branch 261->263: signature is None skips _inject_signature."""

    def sample() -> str: ...

    app = _make_docstring_app(typehints_document_rtype=False)

    lines: list[str] = []
    sat._inject_types_to_docstring({"return": str}, None, sample, app, "function", "sample", lines)  # noqa: SLF001
    assert not any(line.startswith(":type") for line in lines)