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
1 change: 1 addition & 0 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType:
arg_kinds=t.arg_kinds[:-2] + prefix.arg_kinds + t.arg_kinds[-2:],
arg_names=t.arg_names[:-2] + prefix.arg_names + t.arg_names[-2:],
ret_type=t.ret_type.accept(self),
from_concatenate=t.from_concatenate or bool(repl.prefix.arg_types),
)

var_arg = t.var_arg()
Expand Down
17 changes: 10 additions & 7 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ def check_mixed(
type_state.record_negative_subtype_cache_entry(self._subtype_kind, left, right)
return nominal
if right.type.is_protocol and is_protocol_implementation(
left, right, proper_subtype=self.proper_subtype
left, right, proper_subtype=self.proper_subtype, options=self.options
):
return True
# We record negative cache entry here, and not in the protocol check like we do for
Expand Down Expand Up @@ -647,7 +647,7 @@ def visit_param_spec(self, left: ParamSpecType) -> bool:
and right.id == left.id
and right.flavor == left.flavor
):
return True
return self._is_subtype(left.prefix, right.prefix)
if isinstance(right, Parameters) and are_trivial_parameters(right):
return True
return self._is_subtype(left.upper_bound, self.right)
Expand Down Expand Up @@ -696,7 +696,7 @@ def visit_callable_type(self, left: CallableType) -> bool:
ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names,
strict_concatenate=(self.options.extra_checks or self.options.strict_concatenate)
if self.options
else True,
else False,
)
elif isinstance(right, Overloaded):
return all(self._is_subtype(left, item) for item in right.items)
Expand Down Expand Up @@ -863,7 +863,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
strict_concat = (
(self.options.extra_checks or self.options.strict_concatenate)
if self.options
else True
else False
)
if left_index not in matched_overloads and (
is_callable_compatible(
Expand Down Expand Up @@ -1003,6 +1003,7 @@ def is_protocol_implementation(
proper_subtype: bool = False,
class_obj: bool = False,
skip: list[str] | None = None,
options: Options | None = None,
) -> bool:
"""Check whether 'left' implements the protocol 'right'.

Expand Down Expand Up @@ -1068,7 +1069,9 @@ def f(self) -> A: ...
# Nominal check currently ignores arg names
# NOTE: If we ever change this, be sure to also change the call to
# SubtypeVisitor.build_subtype_kind(...) down below.
is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=ignore_names)
is_compat = is_subtype(
subtype, supertype, ignore_pos_arg_names=ignore_names, options=options
)
else:
is_compat = is_proper_subtype(subtype, supertype)
if not is_compat:
Expand All @@ -1080,7 +1083,7 @@ def f(self) -> A: ...
superflags = get_member_flags(member, right)
if IS_SETTABLE in superflags:
# Check opposite direction for settable attributes.
if not is_subtype(supertype, subtype):
if not is_subtype(supertype, subtype, options=options):
return False
if not class_obj:
if IS_SETTABLE not in superflags:
Expand Down Expand Up @@ -1479,7 +1482,7 @@ def are_parameters_compatible(
ignore_pos_arg_names: bool = False,
check_args_covariantly: bool = False,
allow_partial_overlap: bool = False,
strict_concatenate_check: bool = True,
strict_concatenate_check: bool = False,
) -> bool:
"""Helper function for is_callable_compatible, used for Parameter compatibility"""
if right.is_ellipsis_args:
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-overloading.test
Original file line number Diff line number Diff line change
Expand Up @@ -6483,7 +6483,7 @@ P = ParamSpec("P")
R = TypeVar("R")

@overload
def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ...
def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now this error removed in my previous PR is back, LOL.

@overload
def func(x: Callable[P, R]) -> Callable[Concatenate[str, P], R]: ...
def func(x: Callable[..., R]) -> Callable[..., R]: ...
Expand Down
51 changes: 51 additions & 0 deletions test-data/unit/check-parameter-specification.test
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice you use Protocol instead of Generic. While this should not care about that (obviously), mind adding one or shifting a test case over?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add one test case.

Original file line number Diff line number Diff line change
Expand Up @@ -1576,3 +1576,54 @@ def test() -> None: ...
# TODO: avoid this error, although it may be non-trivial.
apply(apply, test) # E: Argument 2 to "apply" has incompatible type "Callable[[], None]"; expected "Callable[P, T]"
[builtins fixtures/paramspec.pyi]

[case testParamSpecPrefixSubtypingInvalid]
from typing import Protocol
from typing_extensions import ParamSpec, Concatenate

P = ParamSpec("P")

class A(Protocol[P]):
def foo(self, *args: P.args, **kwargs: P.kwargs):
...

def bar(b: A[P]) -> A[Concatenate[int, P]]:
return b # E: Incompatible return value type (got "A[P]", expected "A[[int, **P]]")
[builtins fixtures/paramspec.pyi]

[case testParamSpecPrefixSubtypingValidNonStrict]
from typing import Protocol
from typing_extensions import ParamSpec, Concatenate

P = ParamSpec("P")

class A(Protocol[P]):
def foo(self, a: int, *args: P.args, **kwargs: P.kwargs):
...

class B(Protocol[P]):
def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs):
...

def bar(b: B[P]) -> A[Concatenate[int, P]]:
return b
[builtins fixtures/paramspec.pyi]

[case testParamSpecPrefixSubtypingInvalidStrict]
# flags: --extra-checks
from typing import Protocol
from typing_extensions import ParamSpec, Concatenate

P = ParamSpec("P")

class A(Protocol[P]):
def foo(self, a: int, *args: P.args, **kwargs: P.kwargs):
...

class B(Protocol[P]):
def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs):
...

def bar(b: B[P]) -> A[Concatenate[int, P]]:
return b # E: Incompatible return value type (got "B[P]", expected "A[[int, **P]]")
[builtins fixtures/paramspec.pyi]