From ffb7526c823f002c39434182c56e765201f37bfd Mon Sep 17 00:00:00 2001 From: shiftinv <8530778+shiftinv@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:35:54 +0200 Subject: [PATCH] feat(lint): enable ruff `TCH` (flake8-type-checking) rules (#1125) --- disnake/ext/commands/common_bot_base.py | 3 +-- docs/extensions/attributetable.py | 3 ++- docs/extensions/builder.py | 3 ++- docs/extensions/exception_hierarchy.py | 3 ++- docs/extensions/fulltoc.py | 3 ++- docs/extensions/nitpick_file_ignorer.py | 3 ++- docs/extensions/redirects.py | 7 +++++-- docs/extensions/resourcelinks.py | 3 ++- pyproject.toml | 9 ++++++++- tests/ext/commands/test_core.py | 12 +----------- tests/helpers.py | 9 +++++++++ tests/ui/test_action_row.py | 22 ++++++++++++---------- 12 files changed, 48 insertions(+), 32 deletions(-) diff --git a/disnake/ext/commands/common_bot_base.py b/disnake/ext/commands/common_bot_base.py index 841c3df837..f0d8fd5566 100644 --- a/disnake/ext/commands/common_bot_base.py +++ b/disnake/ext/commands/common_bot_base.py @@ -4,6 +4,7 @@ import asyncio import collections.abc +import importlib.machinery import importlib.util import logging import os @@ -19,8 +20,6 @@ from .cog import Cog if TYPE_CHECKING: - import importlib.machinery - from ._types import CoroFunc from .bot import AutoShardedBot, AutoShardedInteractionBot, Bot, InteractionBot from .help import HelpCommand diff --git a/docs/extensions/attributetable.py b/docs/extensions/attributetable.py index 1a66a0c026..d718c73604 100644 --- a/docs/extensions/attributetable.py +++ b/docs/extensions/attributetable.py @@ -14,12 +14,13 @@ from sphinx.util.docutils import SphinxDirective if TYPE_CHECKING: - from _types import SphinxExtensionMeta from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment from sphinx.util.typing import OptionSpec from sphinx.writers.html import HTMLTranslator + from ._types import SphinxExtensionMeta + class attributetable(nodes.General, nodes.Element): pass diff --git a/docs/extensions/builder.py b/docs/extensions/builder.py index 6f1a5493d4..5133af0f85 100644 --- a/docs/extensions/builder.py +++ b/docs/extensions/builder.py @@ -8,12 +8,13 @@ from sphinx.environment.adapters.indexentries import IndexEntries if TYPE_CHECKING: - from _types import SphinxExtensionMeta from docutils import nodes from sphinx.application import Sphinx from sphinx.config import Config from sphinx.writers.html5 import HTML5Translator + from ._types import SphinxExtensionMeta + if TYPE_CHECKING: translator_base = HTML5Translator else: diff --git a/docs/extensions/exception_hierarchy.py b/docs/extensions/exception_hierarchy.py index 147a175af2..67040643ae 100644 --- a/docs/extensions/exception_hierarchy.py +++ b/docs/extensions/exception_hierarchy.py @@ -7,10 +7,11 @@ from docutils.parsers.rst import Directive if TYPE_CHECKING: - from _types import SphinxExtensionMeta from sphinx.application import Sphinx from sphinx.writers.html import HTMLTranslator + from ._types import SphinxExtensionMeta + class exception_hierarchy(nodes.General, nodes.Element): pass diff --git a/docs/extensions/fulltoc.py b/docs/extensions/fulltoc.py index 1d7523e52a..e35cd79514 100644 --- a/docs/extensions/fulltoc.py +++ b/docs/extensions/fulltoc.py @@ -31,7 +31,6 @@ from typing import TYPE_CHECKING, List, cast -from _types import SphinxExtensionMeta from docutils import nodes from sphinx import addnodes @@ -40,6 +39,8 @@ from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment import BuildEnvironment + from ._types import SphinxExtensionMeta + # {prefix: index_doc} mapping # Any document that matches `prefix` will use `index_doc`'s toctree instead. GROUPED_SECTIONS = {"api/": "api/index", "ext/commands/api/": "ext/commands/api/index"} diff --git a/docs/extensions/nitpick_file_ignorer.py b/docs/extensions/nitpick_file_ignorer.py index da967f9d92..cc9eab588f 100644 --- a/docs/extensions/nitpick_file_ignorer.py +++ b/docs/extensions/nitpick_file_ignorer.py @@ -7,9 +7,10 @@ from sphinx.util import logging as sphinx_logging if TYPE_CHECKING: - from _types import SphinxExtensionMeta from sphinx.application import Sphinx + from ._types import SphinxExtensionMeta + class NitpickFileIgnorer(logging.Filter): def __init__(self, app: Sphinx) -> None: diff --git a/docs/extensions/redirects.py b/docs/extensions/redirects.py index fea63483be..44d1c16bef 100644 --- a/docs/extensions/redirects.py +++ b/docs/extensions/redirects.py @@ -1,13 +1,16 @@ # SPDX-License-Identifier: MIT +from __future__ import annotations import json from pathlib import Path -from typing import Dict +from typing import TYPE_CHECKING, Dict -from _types import SphinxExtensionMeta from sphinx.application import Sphinx from sphinx.util.fileutil import copy_asset_file +if TYPE_CHECKING: + from ._types import SphinxExtensionMeta + SCRIPT_PATH = "_templates/api_redirect.js_t" diff --git a/docs/extensions/resourcelinks.py b/docs/extensions/resourcelinks.py index 76a57b4656..d93f6f2715 100644 --- a/docs/extensions/resourcelinks.py +++ b/docs/extensions/resourcelinks.py @@ -10,12 +10,13 @@ from sphinx.util.nodes import split_explicit_title if TYPE_CHECKING: - from _types import SphinxExtensionMeta from docutils.nodes import Node, system_message from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.util.typing import RoleFunction + from ._types import SphinxExtensionMeta + def make_link_role(resource_links: Dict[str, str]) -> RoleFunction: def role( diff --git a/pyproject.toml b/pyproject.toml index a2e0854836..984bdf767f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,7 +149,7 @@ select = [ # "RET", # flake8-return # "SIM", # flake8-simplify "TID251", # flake8-tidy-imports, replaces S404 - # "TCH", # flake8-type-checking + "TCH", # flake8-type-checking "RUF", # ruff specific exceptions "PT", # flake8-pytest-style "Q", # flake8-quotes @@ -198,6 +198,13 @@ ignore = [ # outer loop variables are overwritten by inner assignment target, these are mostly intentional "PLW2901", + # ignore imports that could be moved into type-checking blocks + # (no real advantage other than possibly avoiding cycles, + # but can be dangerous in places where we need to parse signatures) + "TCH001", + "TCH002", + "TCH003", + # temporary disables, to fix later "D205", # blank line required between summary and description "D401", # first line of docstring should be in imperative mood diff --git a/tests/ext/commands/test_core.py b/tests/ext/commands/test_core.py index 1d3076a845..2b29f51988 100644 --- a/tests/ext/commands/test_core.py +++ b/tests/ext/commands/test_core.py @@ -1,20 +1,10 @@ # SPDX-License-Identifier: MIT -from typing import TYPE_CHECKING +from typing_extensions import assert_type from disnake.ext import commands from tests.helpers import reveal_type -if TYPE_CHECKING: - from typing_extensions import assert_type - - # NOTE: using undocumented `expected_text` parameter of pyright instead of `assert_type`, - # as `assert_type` can't handle bound ParamSpecs - reveal_type( - 42, # type: ignore - expected_text="str", # type: ignore - ) - class CustomContext(commands.Context): ... diff --git a/tests/helpers.py b/tests/helpers.py index 2d5a4d8e41..8e22e0cd08 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -16,6 +16,15 @@ def reveal_type(*args, **kwargs) -> None: raise RuntimeError +if TYPE_CHECKING: + # NOTE: using undocumented `expected_text` parameter of pyright instead of `assert_type`, + # as `assert_type` can't handle bound ParamSpecs + reveal_type( + 42, # type: ignore # suppress "revealed type is ..." output + expected_text="str", # type: ignore # ensure the functionality we want still works as expected + ) + + CallableT = TypeVar("CallableT", bound=Callable) diff --git a/tests/ui/test_action_row.py b/tests/ui/test_action_row.py index 9e72ecc3eb..f9c40ffedc 100644 --- a/tests/ui/test_action_row.py +++ b/tests/ui/test_action_row.py @@ -4,17 +4,20 @@ from unittest import mock import pytest +from typing_extensions import assert_type import disnake -from disnake.ui import ActionRow, Button, StringSelect, TextInput, WrappedComponent +from disnake.ui import ( + ActionRow, + Button, + MessageUIComponent, + ModalUIComponent, + StringSelect, + TextInput, + WrappedComponent, +) from disnake.ui.action_row import components_to_dict, components_to_rows -if TYPE_CHECKING: - from typing_extensions import assert_type - - from disnake.ui import MessageUIComponent, ModalUIComponent - - button1 = Button() button2 = Button() button3 = Button() @@ -133,9 +136,8 @@ def test_with_components(self) -> None: row_msg = ActionRow.with_message_components() assert list(row_msg.children) == [] - if TYPE_CHECKING: - assert_type(row_modal, ActionRow[ModalUIComponent]) - assert_type(row_msg, ActionRow[MessageUIComponent]) + assert_type(row_modal, ActionRow[ModalUIComponent]) + assert_type(row_msg, ActionRow[MessageUIComponent]) def test_rows_from_message(self) -> None: rows = [