Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable TypeAlias check by default #101

Merged
merged 6 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ unreleased
statements). The previous checks were disabled by default.
* detect usage of non-integer indices in sys.version_info checks
* extend Y010 to check async functions in addition to normal functions
* introduce Y093 (require using TypeAlias for type aliases)
* introduce Y026 (require using TypeAlias for type aliases)
* introduce Y017 (disallows assignments with multiple targets or non-name targets)
* extend Y001 to cover ParamSpec and TypeVarTuple in addition to TypeVar
* introduce Y018 (detect unused TypeVars)
Expand All @@ -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``)
* all errors are now enabled by default

20.10.0
~~~~~~~
Expand Down
8 changes: 6 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,16 @@ currently emitted:
for more precise type inference.
* 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``.
* Y028: Always use class-based syntax for ``typing.NamedTuple``, instead of
assignment-based syntax.

The following warnings are disabled by default:
Many error codes enforce modern conventions, and some cannot yet be used in
all cases:

* Y093: Type aliases should be explicitly demarcated with ``typing.TypeAlias``.
* 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>`_.

License
-------
Expand Down
43 changes: 9 additions & 34 deletions pyi.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,15 @@ def visit_Assign(self, node: ast.Assign) -> None:
self.typevarlike_defs[target_info] = node
else:
self.error(target, Y001.format(cls_name))
# We avoid triggering Y093 in this case because there are various
# unusual cases where assignment to the result of a call is legitimate
# in stubs.
return
if isinstance(node.value, (ast.Num, ast.Str, ast.Bytes)):
self.error(node.value, Y015)
else:
self.error(node, Y093)
# We avoid triggering Y026 for calls and = ... because there are various
# unusual cases where assignment to the result of a call is legitimate
# in stubs.
elif target_name != "__all__" and not isinstance(
node.value, (ast.Ellipsis, ast.Call)
):
self.error(node, Y026)

def visit_Name(self, node: ast.Name) -> None:
self.all_name_occurrences[node.id] += 1
Expand Down Expand Up @@ -803,8 +804,7 @@ def run(self):
if path.suffix == ".pyi":
visitor = PyiVisitor(filename=path)
for error in visitor.run(self.tree):
if self.should_warn(error.message[:4]):
yield error
yield error

@classmethod
def add_options(cls, parser):
Expand All @@ -830,36 +830,13 @@ def add_options(cls, parser):
except optparse.OptionConflictError:
# In tests, sometimes this option gets called twice for some reason.
pass
parser.extend_default_ignore(DISABLED_BY_DEFAULT)

@classmethod
def parse_options(cls, optmanager, options, extra_args):
"""This is also brittle, only checked with flake8 3.2.1 and master."""
if not options.no_pyi_aware_file_checker:
checker.FileChecker = PyiAwareFileChecker

# Functionality to ignore some warnings. Adapted from flake8-bugbear.
def should_warn(self, code):
"""Returns `True` if flake8-pyi should emit a particular warning.
flake8 overrides default ignores when the user specifies
`ignore = ` in configuration. This is problematic because it means
specifying anything in `ignore = ` implicitly enables all optional
warnings. This function is a workaround for this behavior.
Users should explicitly enable these warnings.
"""
if code[:3] != "Y09":
# Normal warnings are safe for emission.
return True

if self.options is None:
return True

for i in range(2, len(code) + 1):
if code[:i] in self.options.select:
return True

return False


# Please keep error code lists in README and CHANGELOG up to date
Y001 = "Y001 Name of private {} must start with _"
Expand Down Expand Up @@ -892,7 +869,5 @@ def should_warn(self, code):
'Y025 Use "from collections.abc import Set as AbstractSet" '
'to avoid confusion with "builtins.set"'
)
Y026 = "Y026 Use typing_extensions.TypeAlias for type aliases"
Y028 = "Y028 Use class-based syntax for NamedTuples"
Y093 = "Y093 Use typing_extensions.TypeAlias for type aliases"

DISABLED_BY_DEFAULT = [Y093]
3 changes: 1 addition & 2 deletions tests/aliases.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# flags: --select=Y017,Y093
from typing import TypeAlias, ParamSpec as _ParamSpec, _Alias, TypedDict

X = int # Y093 Use typing_extensions.TypeAlias for type aliases
X = int # Y026 Use typing_extensions.TypeAlias for type aliases
X: TypeAlias = int

a = b = int # Y017 Only simple assignments allowed
Expand Down
6 changes: 3 additions & 3 deletions tests/del.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Union
from typing import Union, TypeAlias


ManyStr = list[EitherStr]
EitherStr = Union[str, bytes]
ManyStr: TypeAlias = list[EitherStr]
EitherStr: TypeAlias = Union[str, bytes]


def function(accepts: EitherStr) -> None:
Expand Down
10 changes: 5 additions & 5 deletions tests/forward_refs.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Optional, Union
from typing import Optional, Union, TypeAlias


MaybeCStr = Optional[CStr]
CStr = Union[C, str]
MaybeCStr: TypeAlias = Optional[CStr]
CStr: TypeAlias = Union[C, str]
__version__ = ... # type: str
__author__ = ... # type: str

Expand All @@ -12,14 +12,14 @@ def make_default_c() -> C:


class D(C):
parent = None # type: C
parent: C

def __init__(self) -> None:
...


class C:
other = None # type: C
other: C

def __init__(self) -> None:
...
Expand Down
6 changes: 3 additions & 3 deletions tests/forward_refs_annassign.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Optional, Union
from typing import Optional, Union, TypeAlias


MaybeCStr = Optional[CStr]
CStr = Union[C, str]
MaybeCStr: TypeAlias = Optional[CStr]
CStr: TypeAlias = Union[C, str]
__version__: str
__author__: str

Expand Down
4 changes: 2 additions & 2 deletions tests/quotes.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TypeVar, Literal, Annotated
from typing import TypeVar, Literal, Annotated, TypeAlias

__all__ = ["f", "g"]

Expand All @@ -22,7 +22,7 @@ def j() -> "int": # Y020 Quoted annotations should never be used in stubs
...


Alias = list["int"] # Y020 Quoted annotations should never be used in stubs
Alias: TypeAlias = list["int"] # Y020 Quoted annotations should never be used in stubs

class Child(list["int"]): # Y020 Quoted annotations should never be used in stubs
"""Documented and guaranteed useful.""" # Y021 Docstrings should not be included in stubs
6 changes: 3 additions & 3 deletions tests/vanilla_flake8_not_clean_forward_refs.pyi
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# flags: --no-pyi-aware-file-checker
from typing import Union
from typing import Union, TypeAlias


ManyStr = list[EitherStr] # F821 undefined name 'EitherStr'
EitherStr = Union[str, bytes]
ManyStr: TypeAlias = list[EitherStr] # F821 undefined name 'EitherStr'
EitherStr: TypeAlias = Union[str, bytes]


def function(accepts: EitherStr) -> None:
Expand Down