diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 2fc0144..6f8f1d2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -2,7 +2,7 @@ name: check on: workflow_dispatch: push: - branches: "main" + branches: ["main"] tags-ignore: ["**"] pull_request: schedule: @@ -31,7 +31,7 @@ jobs: with: python-version: "3.12" - name: Install tox - run: python -m pip install tox + run: python -m pip install tox-uv - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -80,7 +80,7 @@ jobs: with: python-version: "3.12" - name: Install tox - run: python -m pip install tox + run: python -m pip install tox-uv - name: Setup coverage tool run: tox -e coverage --notest - name: Install package builder diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f645fb..ef9b2e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,4 +24,4 @@ jobs: - name: Build package run: pyproject-build -s -w . -o dist - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.11 + uses: pypa/gh-action-pypi-publish@v1.8.14 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb7572c..0cb5a7c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,19 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.2.0" + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.1 hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix] - - repo: https://github.com/psf/black - rev: 24.1.1 + - id: check-github-workflows + args: [ "--verbose" ] + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 hooks: - - id: black + - id: codespell + additional_dependencies: ["tomli>=2.0.1"] - repo: https://github.com/tox-dev/tox-ini-fmt rev: "1.3.1" hooks: @@ -22,7 +23,13 @@ repos: rev: "1.7.0" hooks: - id: pyproject-fmt - additional_dependencies: ["tox>=4.11.4"] + additional_dependencies: ["tox>=4.13"] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.3.5" + hooks: + - id: ruff-format + - id: ruff + args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"] - repo: meta hooks: - id: check-hooks-apply diff --git a/README.md b/README.md index c941e93..ff2ff49 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,9 @@ The following configuration options are accepted: `module.for.Class`). If `False`, just the class name displays (e.g. `Class`) - `always_document_param_types` (default: `False`): If `False`, do not add type info for undocumented parameters. If `True`, add stub documentation for undocumented parameters to be able to add type info. +- `always_use_bars_union ` (default: `False`): If `True`, display Union's using the | operator described in PEP 604. + (e.g `X` | `Y` or `int` | `None`). If `False`, Unions will display with the typing in brackets. (e.g. `Union[X, Y]` + or `Optional[int]`) - `typehints_document_rtype` (default: `True`): If `False`, never add an `:rtype:` directive. If `True`, add the `:rtype:` directive if no existing `:rtype:` is found. - `typehints_use_rtype` (default: `True`): Controls behavior when `typehints_document_rtype` is set to `True`. If diff --git a/ignore-words.txt b/ignore-words.txt new file mode 100644 index 0000000..8efad7a --- /dev/null +++ b/ignore-words.txt @@ -0,0 +1,2 @@ +master +manuel diff --git a/pyproject.toml b/pyproject.toml index e593334..8b4eb03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "hatchling.build" requires = [ "hatch-vcs>=0.4", - "hatchling>=1.18", + "hatchling>=1.21.1", ] [project] @@ -41,19 +41,19 @@ dependencies = [ "Sphinx>=7.1.2", ] optional-dependencies.docs = [ - "furo>=2023.9.10", + "furo>=2024.1.29", ] optional-dependencies.numpy = [ "nptyping>=2.5", ] optional-dependencies.testing = [ "covdefaults>=2.3", - "coverage>=7.3.2", - "diff-cover>=8.0.1", - "pytest>=7.4.3", + "coverage>=7.4.2", + "diff-cover>=8.0.3", + "pytest>=8.0.1", "pytest-cov>=4.1", "sphobjinv>=2.3.1", - "typing-extensions>=4.8", + "typing-extensions>=4.9", ] urls.Changelog = "https://github.com/tox-dev/sphinx-autodoc-typehints/blob/main/CHANGELOG.md" urls.Homepage = "https://github.com/tox-dev/sphinx-autodoc-typehints" @@ -68,27 +68,48 @@ version.source = "vcs" line-length = 120 [tool.ruff] -select = ["ALL"] line-length = 120 -target-version = "py37" -isort = {known-first-party = ["tox", "tests"], required-imports = ["from __future__ import annotations"]} -ignore = [ - "ANN101", # no typoe annotation for self - "ANN401", # allow Any as type annotation - "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible - "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible - "S104", # Possible binding to all interface +target-version = "py38" +lint.select = ["ALL"] +lint.isort = { known-first-party = [ + "sphinx_autodoc_typehints", + "tests", +], required-imports = [ + "from __future__ import annotations", +] } +lint.ignore = [ + "ANN101", # no type annotation for self + "ANN401", # allow Any as type annotation + "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible + "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible + "S104", # Possible binding to all interface + "COM812", # Conflict with formatter + "ISC001", # Conflict with formatter + "CPY", # No copyright statements ] -[tool.ruff.per-file-ignores] +lint.preview = true +format.preview = true +format.docstring-code-format = true +format.docstring-code-line-length = 100 +[tool.ruff.lint.per-file-ignores] "tests/**/*.py" = [ - "S101", # asserts allowed in tests... - "FBT", # don"t care about booleans as positional arguments in tests - "INP001", # no implicit namespace - "D", # don"t care about documentation in tests - "S603", # `subprocess` call: check for execution of untrusted input - "PLR2004", # Magic value used in comparison, consider replacing with a constant variable + "S101", # asserts allowed in tests... + "FBT", # don"t care about booleans as positional arguments in tests + "INP001", # no implicit namespace + "D", # don't care about documentation in tests + "S603", # `subprocess` call: check for execution of untrusted input + "PLR2004", # Magic value used in comparison, consider replacing with a constant variable + "PLR0913", # any number of arguments in tests + "PLR0917", # any number of arguments in tests + "PLC2701", # private imports ] +[tool.codespell] +builtin = "clear,usage,en-GB_to_en-US" +ignore-words = "ignore-words.txt" +write-changes = true +count = true + [tool.pytest.ini_options] testpaths = ["tests"] @@ -97,9 +118,12 @@ html.show_contexts = true html.skip_covered = false paths.source = [ "src", - ".tox*/*/lib/python*/site-packages", - ".tox*/pypy*/site-packages", - ".tox*\\*\\Lib\\site-packages", + ".tox/*/lib/python*/site-packages", + ".tox/pypy*/site-packages", + ".tox\\*\\Lib\\site-packages", + ".tox/*/.venv/lib/python*/site-packages", + ".tox/pypy*/.venv/site-packages", + ".tox\\*\\.venv\\Lib\\site-packages", "*/src", "*\\src", ] diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 792e33a..4961fd8 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -67,7 +67,7 @@ def get_annotation_module(annotation: Any) -> str: if ( is_new_type or isinstance(annotation, TypeVar) - or type(annotation).__name__ in ("ParamSpec", "ParamSpecArgs", "ParamSpecKwargs") + or type(annotation).__name__ in {"ParamSpec", "ParamSpecArgs", "ParamSpecKwargs"} ): return "typing" if hasattr(annotation, "__module__"): @@ -107,7 +107,7 @@ def get_annotation_class_name(annotation: Any, module: str) -> str: # noqa: C90 return annotation.__qualname__ # type: ignore[no-any-return] if getattr(annotation, "_name", None): # Required for generic aliases on Python 3.7+ return annotation._name # type: ignore[no-any-return] # noqa: SLF001 - if module in ("typing", "typing_extensions") and isinstance(getattr(annotation, "name", None), str): + if module in {"typing", "typing_extensions"} and isinstance(getattr(annotation, "name", None), str): # Required for at least Pattern and Match return annotation.name # type: ignore[no-any-return] @@ -140,7 +140,7 @@ def get_annotation_args(annotation: Any, module: str, class_name: str) -> tuple[ return () # This is the original, not parametrized type # Special cases - if class_name in ("Pattern", "Match") and hasattr(annotation, "type_var"): # Python < 3.7 + if class_name in {"Pattern", "Match"} and hasattr(annotation, "type_var"): # Python < 3.7 return (annotation.type_var,) if class_name == "ClassVar" and hasattr(annotation, "__type__"): # ClassVar on Python < 3.7 return (annotation.__type__,) @@ -169,7 +169,7 @@ def format_internal_tuple(t: tuple[Any, ...], config: Config) -> str: return f"({', '.join(fmt)})" -def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PLR0911, PLR0912, PLR0915 +def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PLR0911, PLR0912, PLR0915, PLR0914 """ Format the annotation. @@ -238,7 +238,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL formatted_args = None if args else args_format elif full_name == "typing.Optional": args = tuple(x for x in args if x is not type(None)) - elif full_name in ("typing.Union", "types.UnionType") and type(None) in args: + elif full_name in {"typing.Union", "types.UnionType"} and type(None) in args: if len(args) == 2: # noqa: PLR2004 full_name = "typing.Optional" role = "data" @@ -250,7 +250,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL role = "data" args_format = f"\\[:py:data:`{prefix}typing.Union`\\[{{}}]]" args = tuple(x for x in args if x is not type(None)) - elif full_name in ("typing.Callable", "collections.abc.Callable") and args and args[0] is not ...: + elif full_name in {"typing.Callable", "collections.abc.Callable"} and args and args[0] is not ...: fmt = [format_annotation(arg, config) for arg in args] formatted_args = f"\\[\\[{', '.join(fmt[:-1])}], {fmt[-1]}]" elif full_name == "typing.Literal": @@ -316,7 +316,7 @@ def remove_prefix(text: str, prefix: str) -> str: return "\n".join(aligned_prefix + aligned_suffix) -def process_signature( # noqa: C901, PLR0913 +def process_signature( # noqa: C901, PLR0913, PLR0917 app: Sphinx, what: str, name: str, @@ -541,7 +541,7 @@ def _one_child(module: Module) -> stmt | None: comment_args = split_type_comment_args(comment_args_str) is_inline = len(comment_args) == 1 and comment_args[0] == "..." if not is_inline: - if args and args[0].arg in ("self", "cls") and len(comment_args) != len(args): + if args and args[0].arg in {"self", "cls"} and len(comment_args) != len(args): comment_args.insert(0, None) # self/cls may be omitted in type comments, insert blank if len(args) != len(comment_args): @@ -590,9 +590,9 @@ def add(val: str) -> None: brackets, start_arg_at, at = 0, 0, 0 for at, char in enumerate(comment): - if char in ("[", "("): + if char in {"[", "("}: brackets += 1 - elif char in ("]", ")"): + elif char in {"]", ")"}: brackets -= 1 elif char == "," and brackets == 0: add(comment[start_arg_at:at]) @@ -616,7 +616,7 @@ def format_default(app: Sphinx, default: Any, is_annotated: bool) -> str | None: return f"default: ``{formatted}``" -def process_docstring( # noqa: PLR0913 +def process_docstring( # noqa: PLR0913, PLR0917, PLR0917 app: Sphinx, what: str, name: str, @@ -695,7 +695,7 @@ def _line_is_param_line_for_arg(line: str, arg_name: str) -> bool: return any(doc_name == prefix + arg_name for prefix in ("", "\\*", "\\**", "\\*\\*")) -def _inject_types_to_docstring( # noqa: PLR0913 +def _inject_types_to_docstring( # noqa: PLR0913, PLR0917 type_hints: dict[str, Any], signature: inspect.Signature | None, original_obj: Any, @@ -779,6 +779,9 @@ def node_line_no(node: Node) -> int | None: docutils rst parser source code. An example where the node doesn't have a line number but the first child does is all `definition_list` nodes. It seems like bullet_list and option_list get line numbers, but enum_list also doesn't. """ + if node is None: + return None + while node.line is None and node.children: node = node.children[0] return node.line @@ -826,7 +829,7 @@ def get_insert_index(app: Sphinx, lines: list[str]) -> InsertIndexInfo | None: # 4. Insert before examples for child in doc.children: - if tag_name(child) in ["literal_block", "paragraph", "field_list"]: + if tag_name(child) in {"literal_block", "paragraph", "field_list"}: continue line_no = node_line_no(child) at = line_no - 2 if line_no else len(lines) @@ -836,7 +839,7 @@ def get_insert_index(app: Sphinx, lines: list[str]) -> InsertIndexInfo | None: return InsertIndexInfo(insert_index=len(lines)) -def _inject_rtype( # noqa: PLR0913 +def _inject_rtype( # noqa: PLR0913, PLR0917 type_hints: dict[str, Any], original_obj: Any, app: Sphinx, @@ -876,7 +879,7 @@ def _inject_rtype( # noqa: PLR0913 lines.insert(insert_index + 1, "") else: line = lines[insert_index] - lines[insert_index] = f":return: {formatted_annotation} --{line[line.find(' '):]}" + lines[insert_index] = f":return: {formatted_annotation} --{line[line.find(' ') :]}" def validate_config(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None: # noqa: ARG001 @@ -921,8 +924,7 @@ def sphinx_autodoc_typehints_type_role( result in """ unescaped = unescape(text) - # the typestubs for docutils don't have any info about Inliner - doc = parse(unescaped, inliner.document.settings) # type: ignore[attr-defined] + doc = parse(unescaped, inliner.document.settings) n = nodes.inline(text) n["classes"].append("sphinx_autodoc_typehints-type") n += doc.children[0].children @@ -950,6 +952,7 @@ def setup(app: Sphinx) -> dict[str, bool]: __all__ = [ "__version__", + "backfill_type_hints", "format_annotation", "get_annotation_args", "get_annotation_class_name", @@ -957,5 +960,4 @@ def setup(app: Sphinx) -> dict[str, bool]: "normalize_source_lines", "process_docstring", "process_signature", - "backfill_type_hints", ] diff --git a/src/sphinx_autodoc_typehints/attributes_patch.py b/src/sphinx_autodoc_typehints/attributes_patch.py index 439700d..6e4575d 100644 --- a/src/sphinx_autodoc_typehints/attributes_patch.py +++ b/src/sphinx_autodoc_typehints/attributes_patch.py @@ -45,7 +45,7 @@ def _stringify_annotation(app: Sphinx, annotation: Any, mode: str = "") -> str: # noqa: ARG001 # Format the annotation with sphinx-autodoc-typehints and inject our magic prefix to tell our patched # PyAttribute.handle_signature to treat it as rst. - from . import format_annotation + from . import format_annotation # noqa: PLC0415 return TYPE_IS_RST_LABEL + format_annotation(annotation, app.config) diff --git a/src/sphinx_autodoc_typehints/parser.py b/src/sphinx_autodoc_typehints/parser.py index ea38444..1f93881 100644 --- a/src/sphinx_autodoc_typehints/parser.py +++ b/src/sphinx_autodoc_typehints/parser.py @@ -13,13 +13,20 @@ from docutils import nodes from docutils.frontend import Values + from docutils.statemachine import StringList + + +class _RstSnippetParser(RSTParser): + @staticmethod + def decorate(_content: StringList) -> None: + """Override to skip processing rst_epilog/rst_prolog for typing.""" def parse(inputstr: str, settings: Values | optparse.Values) -> nodes.document: """Parse inputstr and return a docutils document.""" doc = new_document("", settings=settings) with sphinx_domains(settings.env): - parser = RSTParser() + parser = _RstSnippetParser() parser.set_application(settings.env.app) parser.parse(inputstr, doc) return doc diff --git a/src/sphinx_autodoc_typehints/patches.py b/src/sphinx_autodoc_typehints/patches.py index 9fea8e2..18714ad 100644 --- a/src/sphinx_autodoc_typehints/patches.py +++ b/src/sphinx_autodoc_typehints/patches.py @@ -28,13 +28,13 @@ def fix_autodoc_typehints_for_overloaded_methods() -> None: See https://github.com/tox-dev/sphinx-autodoc-typehints/issues/296 """ - from sphinx.ext.autodoc import FunctionDocumenter, MethodDocumenter + from sphinx.ext.autodoc import FunctionDocumenter, MethodDocumenter # noqa: PLC0415 del FunctionDocumenter.format_signature del MethodDocumenter.format_signature -def napoleon_numpy_docstring_return_type_processor( # noqa: PLR0913 +def napoleon_numpy_docstring_return_type_processor( # noqa: PLR0913, PLR0917 app: Sphinx, what: str, name: str, # noqa: ARG001 @@ -43,7 +43,7 @@ def napoleon_numpy_docstring_return_type_processor( # noqa: PLR0913 lines: list[str], ) -> None: """Insert a : under Returns: to tell napoleon not to look for a return type.""" - if what not in ["function", "method"]: + if what not in {"function", "method"}: return if not getattr(app.config, "napoleon_numpy_docstring", False): return @@ -52,7 +52,7 @@ def napoleon_numpy_docstring_return_type_processor( # noqa: PLR0913 # Returns: # -------- for pos, line in enumerate(lines[:-2]): - if line.lower().strip(":") not in ["return", "returns"]: + if line.lower().strip(":") not in {"return", "returns"}: continue # Underline detection. chars = set(lines[pos + 1].strip()) diff --git a/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py b/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py index aabdbc4..d5ca701 100644 --- a/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py +++ b/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py @@ -15,9 +15,9 @@ class SomeClass: """Date to handle""" @classmethod - def from_str(cls, input_value: str) -> SomeClass: # noqa: ANN102 + def from_str(cls, input_value: str) -> SomeClass: """ - Initialise from string + Initialize from string :param input_value: Input :return: result @@ -25,9 +25,9 @@ def from_str(cls, input_value: str) -> SomeClass: # noqa: ANN102 return cls(input_value) @classmethod - def from_date(cls, input_value: datetime.date) -> SomeClass: # noqa: ANN102 + def from_date(cls, input_value: datetime.date) -> SomeClass: """ - Initialise from date + Initialize from date :param input_value: Input :return: result @@ -35,16 +35,16 @@ def from_date(cls, input_value: datetime.date) -> SomeClass: # noqa: ANN102 return cls(input_value) @classmethod - def from_time(cls, input_value: datetime.time) -> SomeClass: # noqa: ANN102 + def from_time(cls, input_value: datetime.time) -> SomeClass: """ - Initialise from time + Initialize from time :param input_value: Input :return: result """ return cls(input_value) - def calculate_thing(self, number: float) -> datetime.timedelta: + def calculate_thing(self, number: float) -> datetime.timedelta: # noqa: PLR6301 """ Calculate a thing diff --git a/tests/roots/test-resolve-typing-guard/demo_typing_guard.py b/tests/roots/test-resolve-typing-guard/demo_typing_guard.py index 8b3736d..7d409f3 100644 --- a/tests/roots/test-resolve-typing-guard/demo_typing_guard.py +++ b/tests/roots/test-resolve-typing-guard/demo_typing_guard.py @@ -59,9 +59,9 @@ def func(_x: Literal) -> None: ... __all__ = [ - "a", + "AnotherClass", + "SomeClass", "ValueError", + "a", "cmp_to_key", - "SomeClass", - "AnotherClass", ] diff --git a/tests/test_integration.py b/tests/test_integration.py index 88d25b8..8d732d9 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -175,7 +175,7 @@ def __dunder_method(self, x: str) -> str: :param x: foo """ - def __magic_custom_method__(self, x: str) -> str: + def __magic_custom_method__(self, x: str) -> str: # noqa: PLW3201 """ Magic dunder method docstring. @@ -183,7 +183,7 @@ def __magic_custom_method__(self, x: str) -> str: """ @classmethod - def a_classmethod(cls, x: bool, y: int, z: Optional[str] = None) -> str: # noqa: ANN102, UP007 + def a_classmethod(cls, x: bool, y: int, z: Optional[str] = None) -> str: # noqa: UP007 """ Classmethod docstring. @@ -415,7 +415,7 @@ def __init__( # type: (...) -> None pass - def foo( # noqa: ANN201 + def foo( # noqa: ANN201, PLR6301 self, x, # type: str # noqa: ANN001, ARG002 ): @@ -427,7 +427,7 @@ def foo( # noqa: ANN201 """ return 42 - def method_without_typehint(self, x): # noqa: ANN001, ANN201, ARG002 + def method_without_typehint(self, x): # noqa: ANN001, ANN201, ARG002, PLR6301 """ Method docstring. """ @@ -510,7 +510,7 @@ class ClassWithTypehintsNotInline: def __init__(self, x=None) -> None: # type: (Optional[Callable[[int, bytes], int]]) -> None # noqa: ANN001 pass - def foo(self, x=1): # type: (Callable[[int, bytes], int]) -> int # noqa: ANN001, ANN201 + def foo(self, x=1): # type: (Callable[[int, bytes], int]) -> int # noqa: ANN001, ANN201, PLR6301 """ Method docstring. @@ -520,7 +520,7 @@ def foo(self, x=1): # type: (Callable[[int, bytes], int]) -> int # noqa: ANN00 @classmethod def mk( # noqa: ANN206 - cls, # noqa: ANN102 + cls, x=None, # noqa: ANN001 ): # type: (Optional[Callable[[int, bytes], int]]) -> ClassWithTypehintsNotInline """ @@ -1094,7 +1094,7 @@ def docstring_with_bullet_list_after_params(param: int) -> None: # noqa: ARG001 maybe multiple lines Next Term - Somethign about it + Something about it """, ) @@ -1110,7 +1110,7 @@ def docstring_with_definition_list_after_params(param: int) -> None: # noqa: AR maybe multiple lines Next Term - Somethign about it + Something about it """ @@ -1162,7 +1162,7 @@ def docstring_with_enum_list_after_params(param: int) -> None: # noqa: ARG001 maybe multiple lines Next Term - Somethign about it + Something about it -[ Example ]- """, @@ -1179,7 +1179,7 @@ def docstring_with_definition_list_after_params_no_blank_line(param: int) -> Non maybe multiple lines Next Term - Somethign about it + Something about it .. rubric:: Example """ @@ -1268,14 +1268,100 @@ def typehints_use_signature(a: AsyncGenerator) -> AsyncGenerator: return a +prolog = """ +.. |test_node_start| replace:: {test_node_start} +""".format(test_node_start="test_start") + + +@expected( + """ + mod.docstring_with_multiline_note_after_params_prolog_replace(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + Note: + + Some notes. test_start More notes + + """, + rst_prolog=prolog, +) +def docstring_with_multiline_note_after_params_prolog_replace(param: int) -> None: # noqa: ARG001 + """Do something. + + Args: + param: A parameter. + + Note: + + Some notes. |test_node_start| + More notes + """ + + +epilog = """ +.. |test_node_end| replace:: {test_node_end} +""".format(test_node_end="test_end") + + +@expected( + """ + mod.docstring_with_multiline_note_after_params_epilog_replace(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + Note: + + Some notes. test_end More notes + + """, + rst_epilog=epilog, +) +def docstring_with_multiline_note_after_params_epilog_replace(param: int) -> None: # noqa: ARG001 + """Do something. + + Args: + param: A parameter. + + Note: + + Some notes. |test_node_end| + More notes + """ + + +# Config settings for each test run. +# Config Name: Sphinx Options as Dict. +configs = { + "default_conf": {}, + "prolog_conf": {"rst_prolog": prolog}, + "epilog_conf": { + "rst_epilog": epilog, + }, + "bothlog_conf": { + "rst_prolog": prolog, + "rst_epilog": epilog, + }, +} + + @pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")]) +@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"]) @pytest.mark.sphinx("text", testroot="integration") def test_integration( - app: SphinxTestApp, - status: StringIO, - warning: StringIO, - monkeypatch: pytest.MonkeyPatch, - val: Any, + app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str ) -> None: if isclass(val) and issubclass(val, BaseException): template = AUTO_EXCEPTION @@ -1285,6 +1371,7 @@ def test_integration( template = AUTO_FUNCTION (Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__)) + app.config.__dict__.update(configs[conf_run]) app.config.__dict__.update(val.OPTIONS) monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__]) app.build() diff --git a/tests/test_sphinx_autodoc_typehints.py b/tests/test_sphinx_autodoc_typehints.py index 0b68ce6..3c706e5 100644 --- a/tests/test_sphinx_autodoc_typehints.py +++ b/tests/test_sphinx_autodoc_typehints.py @@ -34,6 +34,7 @@ import typing_extensions from sphinx.application import Sphinx from sphinx.config import Config + from sphinx_autodoc_typehints import ( _resolve_type_guarded_imports, backfill_type_hints, @@ -606,7 +607,9 @@ def test_sphinx_output_default_role(app: SphinxTestApp, status: StringIO) -> Non assert "build succeeded" in status.getvalue() # Build succeeded - contents_lines = (Path(app.srcdir) / "_build/pseudoxml/simple_default_role.pseudoxml").read_text().splitlines() + contents_lines = ( + (Path(app.srcdir) / "_build/pseudoxml/simple_default_role.pseudoxml").read_text(encoding="utf-8").splitlines() + ) list_item_idxs = [i for i, line in enumerate(contents_lines) if line.strip() == ""] foo_param = dedent("\n".join(contents_lines[list_item_idxs[0] : list_item_idxs[1]])) expected_foo_param = """\ @@ -838,7 +841,7 @@ def test_resolve_typing_guard_attrs_imports(app: SphinxTestApp, status: StringIO def test_no_source_code_type_guard() -> None: - from csv import Error + from csv import Error # noqa: PLC0415 _resolve_type_guarded_imports([], Error) diff --git a/tests/test_version.py b/tests/test_version.py index 3c82061..1a97405 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -2,6 +2,6 @@ def test_version() -> None: - from sphinx_autodoc_typehints import __version__ + from sphinx_autodoc_typehints import __version__ # noqa: PLC0415 assert __version__ diff --git a/tox.ini b/tox.ini index 495e497..398d5b0 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,6 @@ env_list = py310 py39 py38 - py37 type coverage readme @@ -39,7 +38,7 @@ commands = description = format the code base to adhere to our styles, and complain about what we cannot do automatically skip_install = true deps = - pre-commit>=3.5 + pre-commit>=3.6.2 commands = pre-commit run --all-files --show-diff-on-failure @@ -51,8 +50,8 @@ extras = [testenv:type] description = run type check on code base deps = - mypy==1.7.1 - types-docutils>=0.20.0.3 + mypy==1.8 + types-docutils>=0.20.0.20240304 set_env = {tty:MYPY_FORCE_COLOR = 1} commands = @@ -64,8 +63,8 @@ description = combine coverage files and generate diff (against DIFF_AGAINST def skip_install = true deps = covdefaults>=2.3 - coverage>=7.3.2 - diff-cover>=8.0.1 + coverage>=7.4.3 + diff-cover>=8.0.3 extras = parallel_show_output = true pass_env = @@ -84,14 +83,13 @@ depends = py310 py39 py38 - py37 [testenv:readme] description = check that the long description is valid (need for PyPI) skip_install = true deps = - build[virtualenv]>=1.0.3 - twine>=4.0.2 + build[virtualenv]>=1.1.1 + twine>=5 extras = commands = pyproject-build -o {envtmpdir} --wheel --sdist . @@ -103,3 +101,4 @@ package = editable commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' +uv_seed = true pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy