diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e72ec5..e39b81e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +Bugfixes +* Don't emit Y053 for long strings inside `Literal` slices or + metadata strings inside `Annotated` slices. + ## 24.6.0 Bugfixes diff --git a/ERRORCODES.md b/ERRORCODES.md index 5a86af6..db82bbd 100644 --- a/ERRORCODES.md +++ b/ERRORCODES.md @@ -66,7 +66,7 @@ The following warnings are currently emitted by default: | Y050 | Prefer `typing_extensions.Never` over `typing.NoReturn` for argument annotations. | Style | Y051 | Y051 detects redundant unions between `Literal` types and builtin supertypes. For example, `Literal[5]` is redundant in the union `int \| Literal[5]`, and `Literal[True]` is redundant in the union `Literal[True] \| bool`. | Redundant code | Y052 | Y052 disallows assignments to constant values where the assignment does not have a type annotation. For example, `x = 0` in the global namespace is ambiguous in a stub, as there are four different types that could be inferred for the variable `x`: `int`, `Final[int]`, `Literal[0]`, or `Final[Literal[0]]`. Enum members are excluded from this check, as are various special assignments such as `__all__` and `__match_args__`. | Correctness -| Y053 | Only string and bytes literals <=50 characters long are permitted. | Style +| Y053 | Only string and bytes literals <=50 characters long are permitted. (There are some exceptions, such as `Literal` subscripts, metadata strings inside `Annotated` subscripts, and strings passed to `@deprecated`.) | Style | Y054 | Only numeric literals with a string representation <=10 characters long are permitted. | Style | Y055 | Unions of the form `type[X] \| type[Y]` can be simplified to `type[X \| Y]`. Similarly, `Union[type[X], type[Y]]` can be simplified to `type[Union[X, Y]]`. | Style | Y056 | Do not call methods such as `.append()`, `.extend()` or `.remove()` on `__all__`. Different type checkers have varying levels of support for calling these methods on `__all__`. Use `+=` instead, which is known to be supported by all major type checkers. | Correctness diff --git a/pyi.py b/pyi.py index 88c7b8a..370d967 100644 --- a/pyi.py +++ b/pyi.py @@ -1563,7 +1563,8 @@ def _visit_typing_Literal(self, node: ast.Subscript) -> None: suggestion = f"Literal[{new_literal_slice}] | None" self.error(analysis.none_members[0], Y061.format(suggestion=suggestion)) - self.visit(node.slice) + with self.long_strings_allowed.enabled(): + self.visit(node.slice) def _visit_slice_tuple(self, node: ast.Tuple, parent: str | None) -> None: if parent == "Union": @@ -1573,7 +1574,7 @@ def _visit_slice_tuple(self, node: ast.Tuple, parent: str | None) -> None: # Allow literals, except in the first argument if len(node.elts) > 1: self.visit(node.elts[0]) - with self.string_literals_allowed.enabled(): + with self.string_literals_allowed.enabled(), self.long_strings_allowed.enabled(): for elt in node.elts[1:]: self.visit(elt) else: diff --git a/tests/defaults.pyi b/tests/defaults.pyi index f7c170f..30826fb 100644 --- a/tests/defaults.pyi +++ b/tests/defaults.pyi @@ -1,6 +1,7 @@ import math import os import sys +from typing import Annotated, Literal, TypeAlias import _typeshed from _typeshed import SupportsRead, SupportsWrite, sentinel @@ -84,3 +85,10 @@ def f40(x: int, /) -> None: ... def f41(x: int, /, y: "int") -> None: ... # Y020 Quoted annotations should never be used in stubs def f42(x: str = "y", /) -> None: ... def f43(x: str = os.pathsep, /) -> None: ... # Y011 Only simple default values allowed for typed arguments + +# Long strings inside `Literal` or `Annotated` slices are okay +def f44(x: Literal["loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"]) -> None: ... +def f55(x: Annotated[str, "metadataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]) -> None: ... + +X: TypeAlias = Literal["llllllllllllllllllllllllllonggggggggggggggggggggggggggggggggggggggggg"] +Y: TypeAlias = Annotated[int, "verrrrrrrrrrrrrrrrrrrry looooooooooooooongggggggggggggggggggggggggggggggggggg"] diff --git a/tests/pep695_py312.pyi b/tests/pep695_py312.pyi index 0832771..6bd0575 100644 --- a/tests/pep695_py312.pyi +++ b/tests/pep695_py312.pyi @@ -3,7 +3,16 @@ import typing from collections.abc import Iterator -from typing import Any, NamedTuple, NoReturn, Protocol, Self, TypedDict +from typing import ( + Annotated, + Any, + Literal, + NamedTuple, + NoReturn, + Protocol, + Self, + TypedDict, +) type lowercase_alias = str | int # Y042 Type aliases should use the CamelCase naming convention type _LooksLikeATypeVarT = str | int # Y043 Bad name for a type alias (the "T" suffix implies a TypeVar) @@ -66,3 +75,6 @@ class _UnusedPEP695Protocol[T](Protocol): # Y046 Protocol "_UnusedPEP695Protoco class _UnusedPEP695TypedDict[T](TypedDict): # Y049 TypedDict "_UnusedPEP695TypedDict" is not used x: T + +type X = Literal["Y053 will not be emitted here despite it being a very long string literal, because it is inside a `Literal` slice"] +type Y = Annotated[int, "look at me, very long string literal, but it's okay because it's an `Annotated` metadata string"]