Skip to content

Commit a113980

Browse files
authored
Ban more imports from typing_extensions as part of Y023 (#459)
1 parent 8fba6ed commit a113980

File tree

9 files changed

+44
-24
lines changed

9 files changed

+44
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Other features:
99
* Support flake8>=7.0.0
1010
* Y061 is no longer emitted in situations where Y062 would also be emitted.
1111
* Improve error message for Y060.
12+
* Y023 now bans more imports from `typing_extensions` now that typeshed has
13+
dropped support for Python 3.7.
1214

1315
Bugfixes:
1416
* Y016: Fix false positive if a method had positional-only parameters (using

pyi.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,26 +125,39 @@ def all_equal(iterable: Iterable[object]) -> bool:
125125
}
126126

127127
# Y023: Import things from typing instead of typing_extensions
128-
# if they're available from the typing module on 3.7+
128+
# if they're available from the typing module on 3.8+
129129
_BAD_TYPINGEXTENSIONS_Y023_IMPORTS = frozenset(
130130
{
131+
"Any",
131132
"AnyStr",
132133
"BinaryIO",
134+
"Final",
133135
"ForwardRef",
134136
"Generic",
135137
"IO",
138+
"Literal",
136139
"Protocol",
137140
"TextIO",
138141
"runtime_checkable",
139142
"NewType",
143+
"SupportsAbs",
144+
"SupportsComplex",
145+
"SupportsFloat",
146+
"SupportsIndex",
147+
"SupportsInt",
148+
"SupportsRound",
149+
"TypedDict",
150+
"TypeVar",
151+
"final",
140152
"overload",
141153
"NoReturn",
142154
# ClassVar deliberately omitted,
143155
# as it's the only one in this group that should be parameterised.
144156
# It is special-cased elsewhere.
145157
#
146-
# Text is also deliberately omitted,
147-
# as you shouldn't be importing it from anywhere! (Y039)
158+
# Text/Optional/Union are also deliberately omitted,
159+
# as well as the various stdlib aliases in typing(_extensions),
160+
# as you shouldn't be importing them from anywhere! (Y039)
148161
}
149162
)
150163

tests/aliases.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ from collections.abc import Mapping
77
from typing import (
88
Annotated,
99
Any,
10+
Literal,
1011
Optional,
1112
ParamSpec as _ParamSpec,
1213
TypeAlias,
@@ -17,7 +18,6 @@ from typing import (
1718
from weakref import WeakValueDictionary
1819

1920
import typing_extensions
20-
from typing_extensions import Literal
2121

2222
class Foo:
2323
def baz(self) -> None: ...
@@ -33,7 +33,7 @@ S = Optional[str] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g.
3333
T = Annotated[int, "some very useful metadata"] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "T: TypeAlias = Annotated[int, 'some very useful metadata']"
3434
U = typing.Literal["ham", "bacon"] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "U: TypeAlias = typing.Literal['ham', 'bacon']"
3535
V = Literal["[(", ")]"] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "V: TypeAlias = Literal['[(', ')]']"
36-
X = typing_extensions.Literal["foo", "bar"] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "X: TypeAlias = typing_extensions.Literal['foo', 'bar']"
36+
X = typing_extensions.Literal["foo", "bar"] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "X: TypeAlias = typing_extensions.Literal['foo', 'bar']" # Y023 Use "typing.Literal" instead of "typing_extensions.Literal"
3737
Y = int | str # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "Y: TypeAlias = int | str"
3838
Z = Union[str, bytes] # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "Z: TypeAlias = Union[str, bytes]"
3939
ZZ = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "ZZ: TypeAlias = None"
@@ -44,7 +44,7 @@ IntArray: TypeAlias = array.array[int]
4444
FooWeakDict: TypeAlias = WeakValueDictionary[str, Foo]
4545
A: typing.TypeAlias = typing.Literal["ham", "bacon"]
4646
B: typing_extensions.TypeAlias = Literal["spam", "eggs"]
47-
C: TypeAlias = typing_extensions.Literal["foo", "bar"]
47+
C: TypeAlias = typing_extensions.Literal["foo", "bar"] # Y023 Use "typing.Literal" instead of "typing_extensions.Literal"
4848
D: TypeAlias = int | str
4949
E: TypeAlias = Union[str, bytes]
5050
F: TypeAlias = int

tests/attribute_annotations.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ field13: Final = b"foo"
4949
field14: Final = True
5050
field15: _Final = True
5151
field16: typing.Final = "foo"
52-
field17: typing_extensions.Final = "foo"
52+
field17: typing_extensions.Final = "foo" # Y023 Use "typing.Final" instead of "typing_extensions.Final"
5353
field18: Final = -24j
5454
field181: Final = field18
5555
field182: Final = os.pathsep
@@ -96,7 +96,7 @@ class Foo:
9696
field12: Final = True
9797
field13: _Final = True
9898
field14: typing.Final = "foo"
99-
field15: typing_extensions.Final = "foo"
99+
field15: typing_extensions.Final = "foo" # Y023 Use "typing.Final" instead of "typing_extensions.Final"
100100
# Standalone strings used to cause issues
101101
field16 = "x" # Y052 Need type annotation for "field16"
102102
if sys.platform == "linux":

tests/calls.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class V(NamedTuple):
1515
# BAD TYPEDDICTS
1616
W = TypedDict("W", {'foo': str, 'bar': int}) # Y031 Use class-based syntax for TypedDicts where possible
1717
B = typing.TypedDict("B", {'foo': str, 'bar': int}) # Y031 Use class-based syntax for TypedDicts where possible
18-
WithTotal = typing_extensions.TypedDict("WithTotal", {'foo': str, 'bar': int}, total=False) # Y031 Use class-based syntax for TypedDicts where possible
18+
WithTotal = typing_extensions.TypedDict("WithTotal", {'foo': str, 'bar': int}, total=False) # Y023 Use "typing.TypedDict" instead of "typing_extensions.TypedDict" # Y031 Use class-based syntax for TypedDicts where possible
1919
BB = mypy_extensions.TypedDict("BB", {'foo': str, 'bar': int}) # Y031 Use class-based syntax for TypedDicts where possible
2020

2121
# we don't want these two to raise errors (type-checkers already do that for us),
@@ -25,7 +25,7 @@ WeirdThirdArg = TypedDict("WeirdThirdArg", {'foo': int, "wot": str}, "who knows?
2525

2626
# GOOD TYPEDDICTS
2727
C = typing.TypedDict("B", {'field has a space': list[int]})
28-
D = typing_extensions.TypedDict("C", {'while': bytes, 'for': int})
28+
D = typing_extensions.TypedDict("C", {'while': bytes, 'for': int}) # Y023 Use "typing.TypedDict" instead of "typing_extensions.TypedDict"
2929
E = TypedDict("D", {'[][]': dict[str, int]})
3030
F = TypedDict("E", {'1': list[str], '2': str})
3131

tests/imports.pyi

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ from re import Match, Pattern
4343
from typing import (
4444
Any,
4545
ClassVar,
46+
Final,
4647
Generic,
4748
Protocol,
4849
TypeVar,
@@ -55,24 +56,22 @@ from typing import (
5556
SupportsRound,
5657
BinaryIO,
5758
IO,
59+
Literal,
5860
NamedTuple,
5961
TextIO,
62+
TypedDict,
6063
AnyStr,
6164
NewType,
6265
NoReturn,
66+
final,
6367
overload,
6468
)
6569
from typing_extensions import (
6670
Concatenate,
67-
Final,
6871
ParamSpec,
69-
SupportsIndex,
70-
final,
71-
Literal,
7272
TypeAlias,
7373
TypeGuard,
7474
Annotated,
75-
TypedDict,
7675
)
7776

7877
# BAD IMPORTS (Y022 code)
@@ -142,6 +141,10 @@ from typing_extensions import Pattern # Y022 Use "re.Pattern[T]" instead of "ty
142141
# BAD IMPORTS (Y023 code)
143142
from typing_extensions import ClassVar # Y023 Use "typing.ClassVar[T]" instead of "typing_extensions.ClassVar[T]"
144143
from typing_extensions import runtime_checkable # Y023 Use "typing.runtime_checkable" instead of "typing_extensions.runtime_checkable"
144+
from typing_extensions import Literal # Y023 Use "typing.Literal" instead of "typing_extensions.Literal"
145+
from typing_extensions import final # Y023 Use "typing.final" instead of "typing_extensions.final"
146+
from typing_extensions import Final # Y023 Use "typing.Final" instead of "typing_extensions.Final"
147+
from typing_extensions import TypedDict # Y023 Use "typing.TypedDict" instead of "typing_extensions.TypedDict"
145148

146149
# BAD IMPORTS: OTHER
147150
from collections import namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
@@ -157,8 +160,8 @@ from collections.abc import ByteString # Y057 Do not use collections.abc.ByteSt
157160
foo: typing.SupportsIndex
158161
baz: re.Pattern[str]
159162

160-
@typing_extensions.final
161-
def bar(arg: collections.abc.Sized) -> typing_extensions.Literal[True]: ...
163+
@typing.final
164+
def bar(arg: collections.abc.Sized) -> typing.Literal[True]: ...
162165

163166
class Fish:
164167
blah: collections.deque[int]
@@ -182,8 +185,10 @@ class Spam:
182185
def meth3(self, g: typing_extensions.AsyncContextManager[Any] = ...) -> None: ... # Y022 Use "contextlib.AbstractAsyncContextManager[T]" instead of "typing_extensions.AsyncContextManager[T]" (PEP 585 syntax)
183186

184187
# BAD ATTRIBUTE ACCESS (Y023 code)
188+
@typing_extensions.final # Y023 Use "typing.final" instead of "typing_extensions.final"
185189
class Foo:
186190
attribute: typing_extensions.ClassVar[int] # Y023 Use "typing.ClassVar[T]" instead of "typing_extensions.ClassVar[T]"
191+
attribute2: typing_extensions.Final[int] # Y023 Use "typing.Final" instead of "typing_extensions.Final"
187192

188193
# BAD ATTRIBUTE ACCESS: OTHER
189194
j: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"

tests/quotes.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ _V = TypeVar()
2525

2626
def make_sure_those_typevars_arent_flagged_as_unused(a: _T, b: _T2, c: _S, d: _U, e: _U2, f: _V) -> tuple[_T, _T2, _S, _U, _U2, _V]: ...
2727

28-
def h(w: Literal["a", "b"], x: typing.Literal["c"], y: typing_extensions.Literal["d"], z: _T) -> _T: ...
28+
def h(w: Literal["a", "b"], x: typing.Literal["c"], y: typing_extensions.Literal["d"], z: _T) -> _T: ... # Y023 Use "typing.Literal" instead of "typing_extensions.Literal"
2929

3030
def i(x: Annotated[int, "lots", "of", "strings"], b: typing.Annotated[str, "more", "strings"]) -> None:
3131
"""Documented and guaranteed useful.""" # Y021 Docstrings should not be included in stubs

tests/union_duplicates.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import builtins
33
import typing
44
from collections.abc import Mapping
55
from typing import ( # Y022 Use "type[MyClass]" instead of "typing.Type[MyClass]" (PEP 585 syntax)
6+
Literal,
67
Type,
78
Union,
89
)
910

1011
import typing_extensions
1112
from typing_extensions import ( # Y022 Use "type[MyClass]" instead of "typing_extensions.Type[MyClass]" (PEP 585 syntax)
12-
Literal,
1313
Type as Type_,
1414
TypeAlias,
1515
)
@@ -49,7 +49,7 @@ def f13_union(x: Union[Type_[int], Type_[str]]) -> None: ...
4949
def f14_union(x: Union[typing_extensions.Type[int], typing_extensions.Type[str]]) -> None: ... # Y022 Use "type[MyClass]" instead of "typing_extensions.Type[MyClass]" (PEP 585 syntax) # Y022 Use "type[MyClass]" instead of "typing_extensions.Type[MyClass]" (PEP 585 syntax)
5050

5151
just_literals_subscript_union: Union[Literal[1], typing.Literal[2]] # Y030 Multiple Literal members in a union. Use a single Literal, e.g. "Literal[1, 2]".
52-
mixed_subscript_union: Union[bytes, Literal['foo'], typing_extensions.Literal['bar']] # Y030 Multiple Literal members in a union. Combine them into one, e.g. "Literal['foo', 'bar']".
52+
mixed_subscript_union: Union[bytes, Literal['foo'], typing_extensions.Literal['bar']] # Y030 Multiple Literal members in a union. Combine them into one, e.g. "Literal['foo', 'bar']". # Y023 Use "typing.Literal" instead of "typing_extensions.Literal"
5353
just_literals_pipe_union: TypeAlias = Literal[True] | Literal['idk'] # Y042 Type aliases should use the CamelCase naming convention # Y030 Multiple Literal members in a union. Use a single Literal, e.g. "Literal[True, 'idk']".
5454
_mixed_pipe_union: TypeAlias = Union[Literal[966], bytes, Literal['baz']] # Y042 Type aliases should use the CamelCase naming convention # Y047 Type alias "_mixed_pipe_union" is not used # Y030 Multiple Literal members in a union. Combine them into one, e.g. "Literal[966, 'baz']".
5555
ManyLiteralMembersButNeedsCombining: TypeAlias = int | Literal['a', 'b'] | Literal['baz'] # Y030 Multiple Literal members in a union. Combine them into one, e.g. "Literal['a', 'b', 'baz']".

tests/unused_things.pyi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import sys
22
import typing
3-
from typing import Protocol, TypedDict, TypeVar
3+
from typing import Literal, Protocol, TypedDict, TypeVar
44

55
import mypy_extensions
66
import typing_extensions
7-
from typing_extensions import Literal, TypeAlias
7+
from typing_extensions import TypeAlias
88

99
_T = TypeVar("_T")
1010

@@ -41,7 +41,7 @@ def uses__UsedAlias(arg: _UsedAlias) -> None: ...
4141
class _UnusedTypedDict(TypedDict): # Y049 TypedDict "_UnusedTypedDict" is not used
4242
foo: str
4343

44-
class _UnusedTypedDict2(typing_extensions.TypedDict): # Y049 TypedDict "_UnusedTypedDict2" is not used
44+
class _UnusedTypedDict2(typing_extensions.TypedDict): # Y049 TypedDict "_UnusedTypedDict2" is not used # Y023 Use "typing.TypedDict" instead of "typing_extensions.TypedDict"
4545
bar: int
4646

4747
class _UnusedTypedDict3(mypy_extensions.TypedDict): # Y049 TypedDict "_UnusedTypedDict3" is not used
@@ -59,7 +59,7 @@ class _UsedTypedDict2(TypedDict):
5959
def uses__UsedTypeDict2(arg: _UsedTypedDict2) -> None: ...
6060

6161
_UnusedTypedDict4 = TypedDict("_UnusedTypedDict4", {"-": int, "def": str}) # Y049 TypedDict "_UnusedTypedDict4" is not used
62-
_UnusedTypedDict5 = typing_extensions.TypedDict("_UnusedTypedDict5", {"foo": bytes, "bar": str}) # Y049 TypedDict "_UnusedTypedDict5" is not used # Y031 Use class-based syntax for TypedDicts where possible
62+
_UnusedTypedDict5 = typing_extensions.TypedDict("_UnusedTypedDict5", {"foo": bytes, "bar": str}) # Y049 TypedDict "_UnusedTypedDict5" is not used # Y023 Use "typing.TypedDict" instead of "typing_extensions.TypedDict" # Y031 Use class-based syntax for TypedDicts where possible
6363
_UsedTypedDict3 = mypy_extensions.TypedDict("_UsedTypedDict3", {".": list[int]})
6464

6565
uses__UsedTypedDict3: _UsedTypedDict3

0 commit comments

Comments
 (0)