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

Extend linter/formatter rules #427

Merged
merged 1 commit into from
Feb 13, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 22 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,27 +71,37 @@ line-length = 120
line-length = 120
target-version = "py37"
lint.select = ["ALL"]
lint.isort = {known-first-party = ["sphinx_autodoc_typehints", "tests"], required-imports = ["from __future__ import annotations"]}
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
"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
]
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]
Expand Down
30 changes: 15 additions & 15 deletions src/sphinx_autodoc_typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__"):
Expand Down Expand Up @@ -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]

Expand Down Expand Up @@ -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__,)
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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"
Expand All @@ -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":
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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])
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -826,7 +826,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)
Expand All @@ -836,7 +836,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,
Expand Down Expand Up @@ -950,12 +950,12 @@ def setup(app: Sphinx) -> dict[str, bool]:

__all__ = [
"__version__",
"backfill_type_hints",
"format_annotation",
"get_annotation_args",
"get_annotation_class_name",
"get_annotation_module",
"normalize_source_lines",
"process_docstring",
"process_signature",
"backfill_type_hints",
]
2 changes: 1 addition & 1 deletion src/sphinx_autodoc_typehints/attributes_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 4 additions & 4 deletions src/sphinx_autodoc_typehints/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SomeClass:
"""Date to handle"""

@classmethod
def from_str(cls, input_value: str) -> SomeClass: # noqa: ANN102
def from_str(cls, input_value: str) -> SomeClass:
"""
Initialize from string

Expand All @@ -25,7 +25,7 @@ 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:
"""
Initialize from date

Expand All @@ -35,7 +35,7 @@ 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:
"""
Initialize from time

Expand All @@ -44,7 +44,7 @@ def from_time(cls, input_value: datetime.time) -> SomeClass: # noqa: ANN102
"""
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

Expand Down
9 changes: 4 additions & 5 deletions tests/roots/test-resolve-typing-guard/demo_typing_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,13 @@ def guarded(self, item: Decimal) -> None:
"""


def func(_x: Literal) -> None:
...
def func(_x: Literal) -> None: ...


__all__ = [
"a",
"AnotherClass",
"SomeClass",
"ValueError",
"a",
"cmp_to_key",
"SomeClass",
"AnotherClass",
]
18 changes: 8 additions & 10 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,15 @@ 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.

:param x: foo
"""

@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.

Expand Down Expand Up @@ -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
):
Expand All @@ -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.
"""
Expand Down Expand Up @@ -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.

Expand All @@ -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
"""
Expand Down Expand Up @@ -625,13 +625,11 @@ def func_with_examples() -> int:


@overload
def func_with_overload(a: int, b: int) -> None:
...
def func_with_overload(a: int, b: int) -> None: ...


@overload
def func_with_overload(a: str, b: str) -> None:
...
def func_with_overload(a: str, b: str) -> None: ...


@expected(
Expand Down
14 changes: 5 additions & 9 deletions tests/test_sphinx_autodoc_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,17 @@ class A:
def get_type(self) -> type:
return type(self)

class Inner:
...
class Inner: ...


class B(Generic[T]):
name = "Foo" # This is set to make sure the correct class name ("B") is picked up


class C(B[str]):
...
class C(B[str]): ...


class D(typing_extensions.Protocol):
...
class D(typing_extensions.Protocol): ...


class E(typing_extensions.Protocol[T]): # type: ignore[misc]
Expand All @@ -105,8 +102,7 @@ class Slotted:
__slots__ = ()


class Metaclass(type):
...
class Metaclass(type): ...


class HintedMethods:
Expand Down Expand Up @@ -843,7 +839,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)

Expand Down