Skip to content

Commit

Permalink
Introduce Y027: Python 2-incompatible import linting (#104)
Browse files Browse the repository at this point in the history
Co-authored-by: Jelle Zijlstra <[email protected]>
  • Loading branch information
AlexWaygood and JelleZijlstra authored Jan 19, 2022
1 parent 487255f commit 34c17bd
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ unreleased
* introduce Y023 (prefer ``typing`` over ``typing_extensions``)
* introduce Y024 (prefer ``typing.NamedTuple`` to ``collections.namedtuple``)
* introduce Y025 (always alias ``collections.abc.Set``)
* introduce Y027 (Python 2-incompatible extension of Y022)
* all errors are now enabled by default

20.10.0
Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ currently emitted:
* Y025: Always alias ``collections.abc.Set`` when importing it, so as to avoid
confusion with ``builtins.set``.
* Y026: Type aliases should be explicitly demarcated with ``typing.TypeAlias``.
* Y027: Same as Y022. Unlike Y022, however, the imports disallowed with this
error code are required if you wish to write Python 2-compatible stubs.
Switch this error code off in your config file if you support Python 2.
* Y028: Always use class-based syntax for ``typing.NamedTuple``, instead of
assignment-based syntax.

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

License
-------
Expand Down
24 changes: 17 additions & 7 deletions pyi.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,8 @@ class TypeVarInfo(NamedTuple):
name: str


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

# typing_extensions.ContextManager is omitted from this collection - special-cased
# typing_extensions.ContextManager is omitted from the Y023 and Y027 collections - special-cased
_BAD_Y023_IMPORTS = frozenset(
{
# collections.abc aliases
Expand All @@ -99,6 +95,12 @@ class TypeVarInfo(NamedTuple):
}
)

_BAD_Y027_IMPORTS = {
"typing.ContextManager": "contextlib.AbstractContextManager",
"typing.OrderedDict": "collections.OrderedDict",
"typing_extensions.OrderedDict": "collections.OrderedDict",
}


class PyiAwareFlakesChecker(FlakesChecker):
def deferHandleNode(self, node, parent):
Expand Down Expand Up @@ -257,6 +259,13 @@ def _check_import_or_attribute(
bad_cls_alias=fullname,
)

# Y027 errors
elif fullname in _BAD_Y027_IMPORTS:
error_message = Y027.format(
good_cls_name=f'"{_BAD_Y027_IMPORTS[fullname]}"',
bad_cls_alias=fullname,
)

# Y023 errors
elif module_name == "typing_extensions":
if object_name in _BAD_Y023_IMPORTS:
Expand Down Expand Up @@ -870,4 +879,5 @@ def parse_options(cls, optmanager, options, extra_args):
'to avoid confusion with "builtins.set"'
)
Y026 = "Y026 Use typing_extensions.TypeAlias for type aliases"
Y027 = 'Y027 Use {good_cls_name} instead of "{bad_cls_alias}"'
Y028 = "Y028 Use class-based syntax for NamedTuples"
11 changes: 9 additions & 2 deletions tests/imports.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ from typing import (
NewType,
NoReturn,
overload,
ContextManager # ContextManager must be importable from typing (but not typing_extensions) for Python 2 compabitility
)
from typing_extensions import (
Concatenate,
Expand All @@ -97,7 +96,6 @@ from typing_extensions import (
TypeGuard,
Annotated,
TypedDict,
OrderedDict # OrderedDict must be importable from typing_extensions (but not typing) for Python 2 compatibility
)


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

# BAD IMPORTS (Y027 code)
from typing import ContextManager # Y027 Use "contextlib.AbstractContextManager" instead of "typing.ContextManager"
from typing import OrderedDict # Y027 Use "collections.OrderedDict" instead of "typing.OrderedDict"
from typing_extensions import OrderedDict # Y027 Use "collections.OrderedDict" instead of "typing_extensions.OrderedDict"

# BAD IMPORTS: OTHER
from collections import namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
from collections.abc import Set # Y025 Use "from collections.abc import Set as AbstractSet" to avoid confusion with "builtins.set"
Expand Down Expand Up @@ -169,5 +172,9 @@ class Foo:
h: typing_extensions.Awaitable[float] # Y023 Use "typing.Awaitable" instead of "typing_extensions.Awaitable"
i: typing_extensions.ContextManager[None] # Y023 Use "contextlib.AbstractContextManager" (or "typing.ContextManager" in Python 2-compatible code) instead of "typing_extensions.ContextManager"

# BAD ATTRIBUTE ACCESS (Y027 code)
k: typing_extensions.OrderedDict[int, str] # Y027 Use "collections.OrderedDict" instead of "typing_extensions.OrderedDict"
l: typing.ContextManager # Y027 Use "contextlib.AbstractContextManager" instead of "typing.ContextManager"

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

0 comments on commit 34c17bd

Please sign in to comment.