Skip to content

Commit

Permalink
Add check for unnecessary-default-type-args
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p committed Sep 20, 2024
1 parent 7aa4436 commit 9641ce2
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 0 deletions.
4 changes: 4 additions & 0 deletions doc/data/messages/u/unnecessary-default-type-args/bad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from collections.abc import AsyncGenerator, Generator

a1: AsyncGenerator[int, None] # [unnecessary-default-type-args]
b1: Generator[int, None, None] # [unnecessary-default-type-args]
6 changes: 6 additions & 0 deletions doc/data/messages/u/unnecessary-default-type-args/details.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
At the moment, this check only works for ``Generator`` and ``AsyncGenerator``.

Starting with Python 3.13, the ``SendType`` and ``ReturnType`` default to ``None``.
As such it's no longer necessary to specify them. The ``collections.abc`` variants
don't validated the number of type arguments. Therefore the defaults for these
can be used in earlier versions as well.
4 changes: 4 additions & 0 deletions doc/data/messages/u/unnecessary-default-type-args/good.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from collections.abc import AsyncGenerator, Generator

a1: AsyncGenerator[int]
b1: Generator[int]
2 changes: 2 additions & 0 deletions doc/data/messages/u/unnecessary-default-type-args/related.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `Python documentation for AsyncGenerator <https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator>`_
- `Python documentation for Generator <https://docs.python.org/3.13/library/typing.html#typing.Generator>`_
3 changes: 3 additions & 0 deletions doc/user_guide/checkers/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ Typing checker Messages
:consider-alternative-union-syntax (R6003): *Consider using alternative Union syntax instead of '%s'%s*
Emitted when 'typing.Union' or 'typing.Optional' is used instead of the
alternative Union syntax 'int | None'.
:unnecessary-default-type-args (R6007): *Type `%s` has unnecessary default type args. Change it to `%s`.*
Emitted when types have default type args which can be omitted. Mainly used
for `typing.Generator` and `typing.AsyncGenerator`.
:redundant-typehint-argument (R6006): *Type `%s` is used more than once in union type annotation. Remove redundant typehints.*
Duplicated type arguments will be skipped by `mypy` tool, therefore should be
removed to avoid confusion.
Expand Down
1 change: 1 addition & 0 deletions doc/user_guide/messages/messages_overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ All messages in the refactor category:
refactor/too-many-statements
refactor/trailing-comma-tuple
refactor/unnecessary-comprehension
refactor/unnecessary-default-type-args
refactor/unnecessary-dict-index-lookup
refactor/unnecessary-list-index-lookup
refactor/use-a-generator
Expand Down
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/9938.new_check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add ``unnecessary-default-type-args`` to the ``typing`` extension to detect the use
of unnecessary default type args for ``typing.Generator`` and ``typing.AsyncGenerator``.

Refs #9938
35 changes: 35 additions & 0 deletions pylint/extensions/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class DeprecatedTypingAliasMsg(NamedTuple):
parent_subscript: bool = False


# pylint: disable-next=too-many-instance-attributes
class TypingChecker(BaseChecker):
"""Find issue specifically related to type annotations."""

Expand Down Expand Up @@ -130,6 +131,12 @@ class TypingChecker(BaseChecker):
"Duplicated type arguments will be skipped by `mypy` tool, therefore should be "
"removed to avoid confusion.",
),
"R6007": (
"Type `%s` has unnecessary default type args. Change it to `%s`.",
"unnecessary-default-type-args",
"Emitted when types have default type args which can be omitted. "
"Mainly used for `typing.Generator` and `typing.AsyncGenerator`.",
),
}
options = (
(
Expand Down Expand Up @@ -174,6 +181,7 @@ def open(self) -> None:
self._py37_plus = py_version >= (3, 7)
self._py39_plus = py_version >= (3, 9)
self._py310_plus = py_version >= (3, 10)
self._py313_plus = py_version >= (3, 13)

self._should_check_typing_alias = self._py39_plus or (
self._py37_plus and self.linter.config.runtime_typing is False
Expand Down Expand Up @@ -248,6 +256,33 @@ def visit_annassign(self, node: nodes.AnnAssign) -> None:

self._check_union_types(types, node)

@only_required_for_messages("unnecessary-default-type-args")
def visit_subscript(self, node: nodes.Subscript) -> None:
inferred = safe_infer(node.value)
if ( # pylint: disable=too-many-boolean-expressions
isinstance(inferred, nodes.ClassDef)
and (
inferred.qname() in {"typing.Generator", "typing.AsyncGenerator"}
and self._py313_plus
or inferred.qname()
in {"_collections_abc.Generator", "_collections_abc.AsyncGenerator"}
)
and isinstance(node.slice, nodes.Tuple)
and all(
(isinstance(el, nodes.Const) and el.value is None)
for el in node.slice.elts[1:]
)
):
suggested_str = (
f"{node.value.as_string()}[{node.slice.elts[0].as_string()}]"
)
self.add_message(
"unnecessary-default-type-args",
args=(node.as_string(), suggested_str),
node=node,
confidence=HIGH,
)

@staticmethod
def _is_deprecated_union_annotation(
annotation: nodes.NodeNG, union_name: str
Expand Down
17 changes: 17 additions & 0 deletions tests/functional/ext/typing/unnecessary_default_type_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# pylint: disable=missing-docstring,deprecated-typing-alias
import collections.abc as ca
import typing as t

a1: t.Generator[int, str, str]
a2: t.Generator[int, None, None]
a3: t.Generator[int]
b1: t.AsyncGenerator[int, str]
b2: t.AsyncGenerator[int, None]
b3: t.AsyncGenerator[int]

c1: ca.Generator[int, str, str]
c2: ca.Generator[int, None, None] # [unnecessary-default-type-args]
c3: ca.Generator[int]
d1: ca.AsyncGenerator[int, str]
d2: ca.AsyncGenerator[int, None] # [unnecessary-default-type-args]
d3: ca.AsyncGenerator[int]
2 changes: 2 additions & 0 deletions tests/functional/ext/typing/unnecessary_default_type_args.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[main]
load-plugins=pylint.extensions.typing
2 changes: 2 additions & 0 deletions tests/functional/ext/typing/unnecessary_default_type_args.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
unnecessary-default-type-args:13:4:13:33::Type `ca.Generator[int, None, None]` has unnecessary default type args. Change it to `ca.Generator[int]`.:HIGH
unnecessary-default-type-args:16:4:16:32::Type `ca.AsyncGenerator[int, None]` has unnecessary default type args. Change it to `ca.AsyncGenerator[int]`.:HIGH
17 changes: 17 additions & 0 deletions tests/functional/ext/typing/unnecessary_default_type_args_py313.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# pylint: disable=missing-docstring,deprecated-typing-alias
import collections.abc as ca
import typing as t

a1: t.Generator[int, str, str]
a2: t.Generator[int, None, None] # [unnecessary-default-type-args]
a3: t.Generator[int]
b1: t.AsyncGenerator[int, str]
b2: t.AsyncGenerator[int, None] # [unnecessary-default-type-args]
b3: t.AsyncGenerator[int]

c1: ca.Generator[int, str, str]
c2: ca.Generator[int, None, None] # [unnecessary-default-type-args]
c3: ca.Generator[int]
d1: ca.AsyncGenerator[int, str]
d2: ca.AsyncGenerator[int, None] # [unnecessary-default-type-args]
d3: ca.AsyncGenerator[int]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[main]
py-version=3.13
load-plugins=pylint.extensions.typing
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
unnecessary-default-type-args:6:4:6:32::Type `t.Generator[int, None, None]` has unnecessary default type args. Change it to `t.Generator[int]`.:HIGH
unnecessary-default-type-args:9:4:9:31::Type `t.AsyncGenerator[int, None]` has unnecessary default type args. Change it to `t.AsyncGenerator[int]`.:HIGH
unnecessary-default-type-args:13:4:13:33::Type `ca.Generator[int, None, None]` has unnecessary default type args. Change it to `ca.Generator[int]`.:HIGH
unnecessary-default-type-args:16:4:16:32::Type `ca.AsyncGenerator[int, None]` has unnecessary default type args. Change it to `ca.AsyncGenerator[int]`.:HIGH

0 comments on commit 9641ce2

Please sign in to comment.