From b2000dadca3f0337e622fb0d1b47852aed9aced5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 1 Apr 2025 20:11:50 -0400 Subject: [PATCH 1/2] Import names from typing directly rather than importing module --- .pre-commit-config.yaml | 2 +- pyproject.toml | 9 +++++++++ stdlib/@tests/test_cases/check_re.py | 12 ++++++------ .../test_cases/typing/check_regression_issue_9296.py | 8 ++++---- stdlib/_typeshed/__init__.pyi | 7 +++---- stdlib/typing_extensions.pyi | 10 +++++----- stubs/click-default-group/click_default_group.pyi | 4 ++-- stubs/click-log/click_log/options.pyi | 11 +++++------ stubs/click-web/click_web/web_click_types.pyi | 10 ++++++---- stubs/corus/corus/third/WikiExtractor.pyi | 4 ++-- 10 files changed, 43 insertions(+), 34 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5cd487b841f9..68efb8b6699b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: name: Run ruff on the test cases args: - "--exit-non-zero-on-fix" - - "--select=FA,I,RUF100" + - "--select=FA,I,ICN001,RUF100,UP007" - "--no-force-exclude" - "--unsafe-fixes" files: '.*test_cases/.+\.py$' diff --git a/pyproject.toml b/pyproject.toml index 7e3e8641f91c..7e0e3c1e6927 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,8 @@ select = [ "FURB169", # Compare the identities of `{object}` and None instead of their respective types "FURB177", # Prefer `Path.cwd()` over `Path().resolve()` for current-directory lookups "FURB187", # Use of assignment of `reversed` on list `{name}` + # Used for lint.flake8-import-conventions.aliases + "ICN001", # `{name}` should be imported as `{asname}` # Autofixable flake8-use-pathlib only "PTH201", # Do not pass the current directory explicitly to `Path` "PTH210", # Invalid suffix passed to `.with_suffix()` @@ -203,6 +205,8 @@ ignore = [ "PLC0414", # Import alias does not rename original package ] "*_pb2.pyi" = [ + # Special autogenerated typing --> typing_extensions aliases + "ICN001", # `{name}` should be imported as `{asname}` # Leave the docstrings as-is, matching source "D", # pydocstyle # See comment on black's force-exclude config above @@ -212,6 +216,11 @@ ignore = [ [tool.ruff.lint.pydocstyle] convention = "pep257" # https://docs.astral.sh/ruff/settings/#lint_pydocstyle_convention +[tool.ruff.lint.flake8-import-conventions.aliases] +# Prevent aliasing these, as it causes false-negatives for certain rules +typing_extensions = "typing_extensions" +typing = "typing" + [tool.ruff.lint.isort] split-on-trailing-comma = false combine-as-imports = true diff --git a/stdlib/@tests/test_cases/check_re.py b/stdlib/@tests/test_cases/check_re.py index b6ab2b0d59d2..93073c1e8e14 100644 --- a/stdlib/@tests/test_cases/check_re.py +++ b/stdlib/@tests/test_cases/check_re.py @@ -2,18 +2,18 @@ import mmap import re -import typing as t +from typing import AnyStr, Match from typing_extensions import assert_type def check_search(str_pat: re.Pattern[str], bytes_pat: re.Pattern[bytes]) -> None: - assert_type(str_pat.search("x"), t.Optional[t.Match[str]]) - assert_type(bytes_pat.search(b"x"), t.Optional[t.Match[bytes]]) - assert_type(bytes_pat.search(bytearray(b"x")), t.Optional[t.Match[bytes]]) - assert_type(bytes_pat.search(mmap.mmap(0, 10)), t.Optional[t.Match[bytes]]) + assert_type(str_pat.search("x"), Match[str] | None) + assert_type(bytes_pat.search(b"x"), Match[bytes] | None) + assert_type(bytes_pat.search(bytearray(b"x")), Match[bytes] | None) + assert_type(bytes_pat.search(mmap.mmap(0, 10)), Match[bytes] | None) -def check_search_with_AnyStr(pattern: re.Pattern[t.AnyStr], string: t.AnyStr) -> re.Match[t.AnyStr]: +def check_search_with_AnyStr(pattern: re.Pattern[AnyStr], string: AnyStr) -> re.Match[AnyStr]: """See issue #9591""" match = pattern.search(string) if match is None: diff --git a/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py b/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py index 34c5631aeb1a..23beaa87ae05 100644 --- a/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py +++ b/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py @@ -1,15 +1,15 @@ from __future__ import annotations -import typing as t +from typing import Any, KeysView, TypeVar -KT = t.TypeVar("KT") +KT = TypeVar("KT") -class MyKeysView(t.KeysView[KT]): +class MyKeysView(KeysView[KT]): pass -d: dict[t.Any, t.Any] = {} +d: dict[Any, Any] = {} dict_keys = type(d.keys()) # This should not cause an error like `Member "register" is unknown`: diff --git a/stdlib/_typeshed/__init__.pyi b/stdlib/_typeshed/__init__.pyi index 99d21b67360a..a503637998d0 100644 --- a/stdlib/_typeshed/__init__.pyi +++ b/stdlib/_typeshed/__init__.pyi @@ -3,7 +3,6 @@ # See the README.md file in this directory for more information. import sys -import typing_extensions from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized from dataclasses import Field from os import PathLike @@ -23,7 +22,7 @@ from typing import ( final, overload, ) -from typing_extensions import Buffer, LiteralString, TypeAlias +from typing_extensions import Buffer, LiteralString, Self as _Self, TypeAlias _KT = TypeVar("_KT") _KT_co = TypeVar("_KT_co", covariant=True) @@ -329,9 +328,9 @@ class structseq(Generic[_T_co]): # The second parameter will accept a dict of any kind without raising an exception, # but only has any meaning if you supply it a dict where the keys are strings. # https://github.com/python/typeshed/pull/6560#discussion_r767149830 - def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> typing_extensions.Self: ... + def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> _Self: ... if sys.version_info >= (3, 13): - def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ... + def __replace__(self, **kwargs: Any) -> _Self: ... # Superset of typing.AnyStr that also includes LiteralString AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString) # noqa: Y001 diff --git a/stdlib/typing_extensions.pyi b/stdlib/typing_extensions.pyi index f3b7b8ddf5b1..5e0f3e904cc5 100644 --- a/stdlib/typing_extensions.pyi +++ b/stdlib/typing_extensions.pyi @@ -1,7 +1,6 @@ import abc import enum import sys -import typing from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import IdentityFunction, Incomplete, Unused from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager @@ -55,6 +54,7 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 Tuple as Tuple, Type as Type, TypedDict as TypedDict, + TypeVar as _TypeVar, Union as Union, ValuesView as ValuesView, _Alias, @@ -196,10 +196,10 @@ __all__ = [ "CapsuleType", ] -_T = typing.TypeVar("_T") -_F = typing.TypeVar("_F", bound=Callable[..., Any]) -_TC = typing.TypeVar("_TC", bound=type[object]) -_T_co = typing.TypeVar("_T_co", covariant=True) # Any type covariant containers. +_T = _TypeVar("_T") +_F = _TypeVar("_F", bound=Callable[..., Any]) +_TC = _TypeVar("_TC", bound=type[object]) +_T_co = _TypeVar("_T_co", covariant=True) # Any type covariant containers. class _Final: ... # This should be imported from typing but that breaks pytype diff --git a/stubs/click-default-group/click_default_group.pyi b/stubs/click-default-group/click_default_group.pyi index 5c73c4dd8db4..83541f3cfbbf 100644 --- a/stubs/click-default-group/click_default_group.pyi +++ b/stubs/click-default-group/click_default_group.pyi @@ -1,5 +1,5 @@ -import typing as t from _typeshed import Incomplete +from collections.abc import Sequence import click @@ -23,7 +23,7 @@ class DefaultCommandFormatter: formatter: click.HelpFormatter mark: str def __init__(self, group: click.Group, formatter: click.HelpFormatter, mark: str = ...) -> None: ... - def write_dl(self, rows: t.Sequence[tuple[str, str]], col_max: int = 30, col_spacing: int = -2) -> None: ... + def write_dl(self, rows: Sequence[tuple[str, str]], col_max: int = 30, col_spacing: int = -2) -> None: ... def __getattr__(self, attr: str) -> Incomplete: ... # __getattr__ used to ala-derive from click.HelpFormatter: # indent_increment: int diff --git a/stubs/click-log/click_log/options.pyi b/stubs/click-log/click_log/options.pyi index e1310ab8ea44..f5fa67d96eac 100644 --- a/stubs/click-log/click_log/options.pyi +++ b/stubs/click-log/click_log/options.pyi @@ -1,12 +1,11 @@ import logging -import typing as t +from collections.abc import Callable +from typing import Any, TypeVar from typing_extensions import TypeAlias import click -_AnyCallable: TypeAlias = t.Callable[..., t.Any] -_FC = t.TypeVar("_FC", bound=_AnyCallable | click.Command) +_AnyCallable: TypeAlias = Callable[..., Any] +_FC = TypeVar("_FC", bound=_AnyCallable | click.Command) -def simple_verbosity_option( - logger: logging.Logger | str | None = None, *names: str, **kwargs: t.Any -) -> t.Callable[[_FC], _FC]: ... +def simple_verbosity_option(logger: logging.Logger | str | None = None, *names: str, **kwargs: Any) -> Callable[[_FC], _FC]: ... diff --git a/stubs/click-web/click_web/web_click_types.pyi b/stubs/click-web/click_web/web_click_types.pyi index 1546f7e7d7d1..7f093372ca9d 100644 --- a/stubs/click-web/click_web/web_click_types.pyi +++ b/stubs/click-web/click_web/web_click_types.pyi @@ -1,17 +1,19 @@ import re -import typing as t +from typing import TypeVar import click +_T = TypeVar("_T") + class EmailParamType(click.ParamType): EMAIL_REGEX: re.Pattern[str] - def convert(self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None) -> t.Any: ... + def convert(self, value: str, param: click.Parameter | None, ctx: click.Context | None) -> str: ... class PasswordParamType(click.ParamType): - def convert(self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None) -> t.Any: ... + def convert(self, value: _T, param: click.Parameter | None, ctx: click.Context | None) -> _T: ... class TextAreaParamType(click.ParamType): - def convert(self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None) -> t.Any: ... + def convert(self, value: _T, param: click.Parameter | None, ctx: click.Context | None) -> _T: ... EMAIL_TYPE: EmailParamType PASSWORD_TYPE: PasswordParamType diff --git a/stubs/corus/corus/third/WikiExtractor.pyi b/stubs/corus/corus/third/WikiExtractor.pyi index b8ee370c8a26..bda32a5d62a1 100644 --- a/stubs/corus/corus/third/WikiExtractor.pyi +++ b/stubs/corus/corus/third/WikiExtractor.pyi @@ -1,4 +1,3 @@ -import typing from _typeshed import Incomplete from collections.abc import Generator from math import ( @@ -14,6 +13,7 @@ from math import ( tan as tan, trunc as trunc, ) +from typing import TypeVar PY2: Incomplete text_type = str @@ -52,7 +52,7 @@ quote_quote: Incomplete spaces: Incomplete dots: Incomplete -_T = typing.TypeVar("_T") +_T = TypeVar("_T") class Template(list[_T]): @classmethod From 030dbeb74e27757752ac1de4b4cf1430bffe2088 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 1 Apr 2025 20:25:38 -0400 Subject: [PATCH 2/2] Leave Optional in test_cases --- .pre-commit-config.yaml | 2 +- stdlib/@tests/test_cases/check_re.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68efb8b6699b..985980915f67 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: name: Run ruff on the test cases args: - "--exit-non-zero-on-fix" - - "--select=FA,I,ICN001,RUF100,UP007" + - "--select=FA,I,ICN001,RUF100" - "--no-force-exclude" - "--unsafe-fixes" files: '.*test_cases/.+\.py$' diff --git a/stdlib/@tests/test_cases/check_re.py b/stdlib/@tests/test_cases/check_re.py index 93073c1e8e14..dee87b474fe2 100644 --- a/stdlib/@tests/test_cases/check_re.py +++ b/stdlib/@tests/test_cases/check_re.py @@ -2,15 +2,15 @@ import mmap import re -from typing import AnyStr, Match +from typing import AnyStr, Match, Optional from typing_extensions import assert_type def check_search(str_pat: re.Pattern[str], bytes_pat: re.Pattern[bytes]) -> None: - assert_type(str_pat.search("x"), Match[str] | None) - assert_type(bytes_pat.search(b"x"), Match[bytes] | None) - assert_type(bytes_pat.search(bytearray(b"x")), Match[bytes] | None) - assert_type(bytes_pat.search(mmap.mmap(0, 10)), Match[bytes] | None) + assert_type(str_pat.search("x"), Optional[Match[str]]) + assert_type(bytes_pat.search(b"x"), Optional[Match[bytes]]) + assert_type(bytes_pat.search(bytearray(b"x")), Optional[Match[bytes]]) + assert_type(bytes_pat.search(mmap.mmap(0, 10)), Optional[Match[bytes]]) def check_search_with_AnyStr(pattern: re.Pattern[AnyStr], string: AnyStr) -> re.Match[AnyStr]: