Skip to content
Open
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
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ flake8-pyi uses Calendar Versioning (CalVer).

## Unreleased

### New Error Codes

* Y069: Flags imports of `typing.NoReturn` and `typing_extensions.NoReturn`
as they are now unconditionally deprecated.

### Removed Error Codes

* Y050: Old error code that flagged `typing.NoReturn` and
`typing_extensions.NoReturn` in argument positions only.

### Other changes

* Support Python 3.15.
* Recommend to use `typing.TypeAlias` instead of `typing_extensions.TypeAlias`.
* Y026: Recommend to use `typing.TypeAlias` instead of `typing_extensions.TypeAlias`.

## 26.5.0

Expand Down
2 changes: 1 addition & 1 deletion ERRORCODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ The following warnings are currently emitted by default:
| <a id="Y047" href="#Y047">Y047</a> | A private `TypeAlias` should be used at least once in the file in which it is defined. | Redundant code
| <a id="Y048" href="#Y048">Y048</a> | Function bodies should contain exactly one statement. This is because stub files are never executed at runtime, so any more than one statement would be redundant. (Note that if a function body includes a docstring, the docstring counts as a "statement".) | Understanding stubs
| <a id="Y049" href="#Y049">Y049</a> | A private `TypedDict` should be used at least once in the file in which it is defined. | Redundant code
| <a id="Y050" href="#Y050">Y050</a> | Prefer `typing_extensions.Never` over `typing.NoReturn` for argument annotations. | Style
| <a id="Y051" href="#Y051">Y051</a> | 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
| <a id="Y052" href="#Y052">Y052</a> | 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
| <a id="Y053" href="#Y053">Y053</a> | 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
Expand All @@ -82,6 +81,7 @@ The following warnings are currently emitted by default:
| <a id="Y066" href="#Y066">Y066</a> | When using if/else with `sys.version_info`, put the code for new Python versions first. | Style
| <a id="Y067" href="#Y067">Y067</a> | Don't use `Incomplete \| None = None` in argument annotations. Instead, just use `=None`. | Style
| <a id="Y068" href="#Y068">Y068</a> | Don't use `@override` in stub files. Problems with a function signature deviating from its superclass are inherited from the implementation, and other tools such as stubtest are better placed to recognize deviations between stubs and the implementation. | Understanding stubs
| <a id="Y069" href="#Y069">Y069</a> | Prefer `typing_extensions.Never` over `typing.NoReturn`. | Style

## Warnings disabled by default

Expand Down
4 changes: 1 addition & 3 deletions flake8_pyi/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@ class Error(NamedTuple):
Y047 = 'Y047 Type alias "{alias_name}" is not used'
Y048 = "Y048 Function body should contain exactly one statement"
Y049 = 'Y049 TypedDict "{typeddict_name}" is not used'
Y050 = (
'Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations'
)
Y051 = 'Y051 "{literal_subtype}" is redundant in a union with "{builtin_supertype}"'
Y052 = 'Y052 Need type annotation for "{variable}"'
Y053 = "Y053 String and bytes literals >50 characters long are not permitted"
Expand Down Expand Up @@ -128,6 +125,7 @@ class Error(NamedTuple):
)
Y067 = 'Y067 Use "=None" instead of "Incomplete | None = None"'
Y068 = 'Y068 Do not use "@override" in stub files.'
Y069 = 'Y069 Use "typing_extensions.Never" instead of "{module}.NoReturn"'

Y090 = (
'Y090 "{original}" means '
Expand Down
8 changes: 4 additions & 4 deletions flake8_pyi/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,6 @@ def _is_object(node: ast.AST | None, name: str, *, from_: Container[str]) -> boo
_is_AsyncIterable = partial(
_is_object, name="AsyncIterable", from_=_TYPING_OR_COLLECTIONS_ABC
)
_is_Protocol = partial(_is_object, name="Protocol", from_=_TYPING_MODULES)
_is_NoReturn = partial(_is_object, name="NoReturn", from_=_TYPING_MODULES)
_is_Final = partial(_is_object, name="Final", from_=_TYPING_MODULES)
_is_Generator = partial(_is_object, name="Generator", from_=_TYPING_OR_COLLECTIONS_ABC)
_is_AsyncGenerator = partial(
Expand Down Expand Up @@ -812,6 +810,10 @@ def _check_import_or_attribute(
if object_name == "Text":
return errors.Y039.format(module=module_name)

# Y069 errors
if object_name == "NoReturn":
return errors.Y069.format(module=module_name)

# Y023 errors
if module_name == "typing_extensions":
if object_name in _BAD_TYPINGEXTENSIONS_Y023_IMPORTS:
Expand Down Expand Up @@ -2065,8 +2067,6 @@ def _visit_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
self.check_for_override(node)

def visit_arg(self, node: ast.arg) -> None:
if _is_NoReturn(node.annotation):
self.error(node, errors.Y050)
if _is_Incomplete(node.annotation):
self.error(node, errors.Y065.format(what=f'parameter "{node.arg}"'))
with self.visiting_arg.enabled():
Expand Down
6 changes: 4 additions & 2 deletions tests/imports.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ from typing import (
TypedDict,
AnyStr,
NewType,
NoReturn,
final,
overload,
)
from typing_extensions import (
Annotated,
Concatenate,
Never,
ParamSpec,
TypeGuard,
Annotated,
)

# BAD IMPORTS (Y022 code)
Expand Down Expand Up @@ -153,6 +153,8 @@ from typing import AbstractSet # Y038 Use "from collections.abc import Set as A
from typing_extensions import AbstractSet # Y038 Use "from collections.abc import Set as AbstractSet" instead of "from typing_extensions import AbstractSet" (PEP 585 syntax)
from typing import Text # Y039 Use "str" instead of "typing.Text"
from typing_extensions import Text # Y039 Use "str" instead of "typing_extensions.Text"
from typing import NoReturn # Y069 Use "typing_extensions.Never" instead of "typing.NoReturn"
from typing_extensions import NoReturn # Y069 Use "typing_extensions.Never" instead of "typing_extensions.NoReturn"
from typing import ByteString # Y057 Do not use typing.ByteString, which has unclear semantics and is deprecated
from collections.abc import ByteString # Y057 Do not use collections.abc.ByteString, which has unclear semantics and is deprecated

Expand Down
19 changes: 14 additions & 5 deletions tests/never_vs_noreturn.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
# isort: skip_file
import typing
from typing import NoReturn

from typing import NoReturn # Y069 Use "typing_extensions.Never" instead of "typing.NoReturn"

import typing_extensions
from typing_extensions import Never
from typing_extensions import NoReturn as NR # Y069 Use "typing_extensions.Never" instead of "typing_extensions.NoReturn"

# NoReturn is now flagged at import time, so it should never be flagged elsewhere.

x: NR

def badfunc0(arg: NoReturn) -> None: ... # Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations
def badfunc1(*args: typing.NoReturn) -> None: ... # Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations
def badfunc2(**kwargs: typing_extensions.NoReturn) -> None: ... # Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations # Y023 Use "typing.NoReturn" instead of "typing_extensions.NoReturn"
def badfunc3(*, arg: NoReturn) -> None: ... # Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations
def badfunc0(arg: NoReturn) -> None: ...
def badfunc1(*args: typing.NoReturn) -> None: ... # Y069 Use "typing_extensions.Never" instead of "typing.NoReturn"
def badfunc2(**kwargs: typing_extensions.NoReturn) -> None: ... # Y069 Use "typing_extensions.Never" instead of "typing_extensions.NoReturn"
def badfunc3(*, arg: NoReturn) -> None: ...
def badfunc4() -> NoReturn: ...

def goodfunc0(arg: Never) -> None: ...
def goodfunc1(*args: typing.Never) -> None: ...
def goodfunc2(**kwargs: typing_extensions.Never) -> None: ...
def goodfunc3(*, arg: Never) -> None: ...
def goodfunc4() -> Never: ...
2 changes: 0 additions & 2 deletions tests/pep695_py312.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ from typing import (
Any,
Literal,
NamedTuple,
NoReturn,
Protocol,
Self,
TypedDict,
Expand All @@ -30,7 +29,6 @@ class GenericPEP695Class[T]:
pass # Y012 Class body must not contain "pass"
def __exit__(self, *args: Any) -> None: ... # Y036 Badly defined __exit__ method: Star-args in an __exit__ method should be annotated with "object", not "Any"
async def __aexit__(self) -> None: ... # Y036 Badly defined __aexit__ method: If there are no star-args, there should be at least 3 non-keyword-only args in an __aexit__ method (excluding "self")
def never_call_me(self, arg: NoReturn) -> None: ... # Y050 Use "typing_extensions.Never" instead of "NoReturn" for argument annotations

class GenericPEP695InheritingFromObject[T](object): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
x: T
Expand Down
Loading