Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
The specification requires positional arguments
> Type checkers should assume that type narrowing should be applied to the expression that is passed as the first positional argument to a user-defined type guard. [...]
>
> If a type guard function is implemented as an instance method or class method, the first positional argument maps to the second parameter (after “self” or “cls”).
  • Loading branch information
A5rocks committed Dec 3, 2022
commit a29a6ec49add9c1849853fb3d12551f00a3bc5d6
13 changes: 13 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,19 @@ def analyze_func_def(self, defn: FuncDef) -> None:
return
assert isinstance(result, ProperType)
if isinstance(result, CallableType):
# type guards need to have a positional argument, to spec
Comment thread
A5rocks marked this conversation as resolved.
if (
result.type_guard
and ARG_POS not in result.arg_kinds[self.is_class_scope() :]
):
self.fail(
"TypeGuard functions must have a positional argument",
result,
code=codes.VALID_TYPE,
)
# in this case, we just kind of just ... remove the type guard.
result = result.copy_modified(type_guard=None)

result = self.remove_unpack_kwargs(defn, result)
if has_self_type and self.type is not None:
info = self.type
Expand Down
33 changes: 31 additions & 2 deletions test-data/unit/check-typeguard.test
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ reveal_type(foo) # N: Revealed type is "def (a: builtins.object) -> TypeGuard[b
[case testTypeGuardCallArgsNone]
from typing_extensions import TypeGuard
class Point: pass
# TODO: error on the 'def' line (insufficient args for type guard)
def is_point() -> TypeGuard[Point]: pass

def is_point() -> TypeGuard[Point]: pass # E: TypeGuard functions must have a positional argument
def main(a: object) -> None:
if is_point():
reveal_type(a) # N: Revealed type is "builtins.object"
Expand Down Expand Up @@ -597,3 +597,32 @@ def func(names: Tuple[str, ...]):
if is_two_element_tuple(names):
reveal_type(names) # N: Revealed type is "Tuple[builtins.str, builtins.str]"
[builtins fixtures/tuple.pyi]

[case testTypeGuardErroneousDefinitionFails]
from typing_extensions import TypeGuard

class Z:
def typeguard(self, *, x: object) -> TypeGuard[int]: # E: TypeGuard functions must have a positional argument
...

def bad_typeguard(*, x: object) -> TypeGuard[int]: # E: TypeGuard functions must have a positional argument
...
[builtins fixtures/tuple.pyi]

[case testTypeGuardWithKeywordArg-xfail]
from typing_extensions import TypeGuard

class Z:
def typeguard(self, x: object) -> TypeGuard[int]:
...

def typeguard(x: object) -> TypeGuard[int]:
...

n: object
if typeguard(x=n):
reveal_type(n) # N: Revealed type is "builtins.int"

if Z().typeguard(x=n):
reveal_type(n) # N: Revealed type is "builtins.int"
[builtins fixtures/tuple.pyi]