Skip to content

Commit 34c17bd

Browse files
Introduce Y027: Python 2-incompatible import linting (#104)
Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 487255f commit 34c17bd

File tree

4 files changed

+32
-9
lines changed

4 files changed

+32
-9
lines changed

CHANGELOG.rst

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ unreleased
2828
* introduce Y023 (prefer ``typing`` over ``typing_extensions``)
2929
* introduce Y024 (prefer ``typing.NamedTuple`` to ``collections.namedtuple``)
3030
* introduce Y025 (always alias ``collections.abc.Set``)
31+
* introduce Y027 (Python 2-incompatible extension of Y022)
3132
* all errors are now enabled by default
3233

3334
20.10.0

README.rst

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ currently emitted:
9292
* Y025: Always alias ``collections.abc.Set`` when importing it, so as to avoid
9393
confusion with ``builtins.set``.
9494
* Y026: Type aliases should be explicitly demarcated with ``typing.TypeAlias``.
95+
* Y027: Same as Y022. Unlike Y022, however, the imports disallowed with this
96+
error code are required if you wish to write Python 2-compatible stubs.
97+
Switch this error code off in your config file if you support Python 2.
9598
* Y028: Always use class-based syntax for ``typing.NamedTuple``, instead of
9699
assignment-based syntax.
97100

@@ -101,6 +104,8 @@ all cases:
101104
* Y026 is incompatible with the pytype type checker and should be turned
102105
off for stubs that need to be compatible with pytype. A fix is tracked
103106
`here <https://github.com/google/pytype/issues/787>`_.
107+
* Y027 is incompatible with Python 2 and should only be used in stubs
108+
that are meant only for Python 3.
104109

105110
License
106111
-------

pyi.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,8 @@ class TypeVarInfo(NamedTuple):
4747
name: str
4848

4949

50-
# OrderedDict is omitted from this blacklist:
51-
# -- In Python 3, we'd rather import it from collections, not typing or typing_extensions
52-
# -- But in Python 2, it cannot be imported from collections or typing, only from typing_extensions
53-
#
54-
# ChainMap does not exist in typing or typing_extensions in Python 2,
55-
# so we can disallow importing it from anywhere except collections
50+
# ChainMap and AsyncContextManager do not exist in typing or typing_extensions in Python 2,
51+
# so we can disallow importing them from anywhere except collections and contextlib respectively.
5652
_BAD_Y022_IMPORTS = {
5753
# typing aliases for collections
5854
"typing.Counter": "collections.Counter",
@@ -79,7 +75,7 @@ class TypeVarInfo(NamedTuple):
7975
"typing_extensions.AsyncContextManager": "contextlib.AbstractAsyncContextManager",
8076
}
8177

82-
# typing_extensions.ContextManager is omitted from this collection - special-cased
78+
# typing_extensions.ContextManager is omitted from the Y023 and Y027 collections - special-cased
8379
_BAD_Y023_IMPORTS = frozenset(
8480
{
8581
# collections.abc aliases
@@ -99,6 +95,12 @@ class TypeVarInfo(NamedTuple):
9995
}
10096
)
10197

98+
_BAD_Y027_IMPORTS = {
99+
"typing.ContextManager": "contextlib.AbstractContextManager",
100+
"typing.OrderedDict": "collections.OrderedDict",
101+
"typing_extensions.OrderedDict": "collections.OrderedDict",
102+
}
103+
102104

103105
class PyiAwareFlakesChecker(FlakesChecker):
104106
def deferHandleNode(self, node, parent):
@@ -257,6 +259,13 @@ def _check_import_or_attribute(
257259
bad_cls_alias=fullname,
258260
)
259261

262+
# Y027 errors
263+
elif fullname in _BAD_Y027_IMPORTS:
264+
error_message = Y027.format(
265+
good_cls_name=f'"{_BAD_Y027_IMPORTS[fullname]}"',
266+
bad_cls_alias=fullname,
267+
)
268+
260269
# Y023 errors
261270
elif module_name == "typing_extensions":
262271
if object_name in _BAD_Y023_IMPORTS:
@@ -870,4 +879,5 @@ def parse_options(cls, optmanager, options, extra_args):
870879
'to avoid confusion with "builtins.set"'
871880
)
872881
Y026 = "Y026 Use typing_extensions.TypeAlias for type aliases"
882+
Y027 = 'Y027 Use {good_cls_name} instead of "{bad_cls_alias}"'
873883
Y028 = "Y028 Use class-based syntax for NamedTuples"

tests/imports.pyi

+9-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ from typing import (
8484
NewType,
8585
NoReturn,
8686
overload,
87-
ContextManager # ContextManager must be importable from typing (but not typing_extensions) for Python 2 compabitility
8887
)
8988
from typing_extensions import (
9089
Concatenate,
@@ -97,7 +96,6 @@ from typing_extensions import (
9796
TypeGuard,
9897
Annotated,
9998
TypedDict,
100-
OrderedDict # OrderedDict must be importable from typing_extensions (but not typing) for Python 2 compatibility
10199
)
102100

103101

@@ -116,6 +114,11 @@ from typing_extensions import ClassVar # Y023 Use "typing.ClassVar" instead of
116114
from typing_extensions import Awaitable # Y023 Use "typing.Awaitable" instead of "typing_extensions.Awaitable"
117115
from typing_extensions import ContextManager # Y023 Use "contextlib.AbstractContextManager" (or "typing.ContextManager" in Python 2-compatible code) instead of "typing_extensions.ContextManager"
118116

117+
# BAD IMPORTS (Y027 code)
118+
from typing import ContextManager # Y027 Use "contextlib.AbstractContextManager" instead of "typing.ContextManager"
119+
from typing import OrderedDict # Y027 Use "collections.OrderedDict" instead of "typing.OrderedDict"
120+
from typing_extensions import OrderedDict # Y027 Use "collections.OrderedDict" instead of "typing_extensions.OrderedDict"
121+
119122
# BAD IMPORTS: OTHER
120123
from collections import namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
121124
from collections.abc import Set # Y025 Use "from collections.abc import Set as AbstractSet" to avoid confusion with "builtins.set"
@@ -169,5 +172,9 @@ class Foo:
169172
h: typing_extensions.Awaitable[float] # Y023 Use "typing.Awaitable" instead of "typing_extensions.Awaitable"
170173
i: typing_extensions.ContextManager[None] # Y023 Use "contextlib.AbstractContextManager" (or "typing.ContextManager" in Python 2-compatible code) instead of "typing_extensions.ContextManager"
171174

175+
# BAD ATTRIBUTE ACCESS (Y027 code)
176+
k: typing_extensions.OrderedDict[int, str] # Y027 Use "collections.OrderedDict" instead of "typing_extensions.OrderedDict"
177+
l: typing.ContextManager # Y027 Use "contextlib.AbstractContextManager" instead of "typing.ContextManager"
178+
172179
# BAD ATTRIBUTE ACCESS: OTHER
173180
j: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"

0 commit comments

Comments
 (0)