Skip to content

Commit

Permalink
Fix crash on ParamSpec with Any callable (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Jun 3, 2024
1 parent 9e40172 commit 57fb3fd
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Unreleased

- Fix various issues with Python 3.13 and 3.14 support (#773)
- Improve `ParamSpec` support (#772)
- Improve `ParamSpec` support (#772, #777)
- Fix handling of stub functions with positional-only parameters with
defaults (#769)
- Recognize exhaustive pattern matching (#766)
Expand Down
5 changes: 5 additions & 0 deletions pyanalyze/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,11 @@ def substitute_typevars(self, typevars: TypeVarMap) -> "Signature":
elif isinstance(new_val, AnyValue):
new_param = SigParameter(param.name, ParameterKind.ELLIPSIS)
params.append((param.name, new_param))
elif isinstance(new_val, CallValue):
new_param = SigParameter(
param.name, ParameterKind.PARAM_SPEC, annotation=new_val
)
params.append((param.name, new_param))
else:
assert isinstance(new_val, CallableValue), new_val
assert isinstance(new_val.signature, Signature), new_val
Expand Down
39 changes: 39 additions & 0 deletions pyanalyze/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,45 @@ def capybara() -> None:
assert_type(apply(sample, 1), str)
apply(sample, "x") # E: incompatible_call

@assert_passes()
def test_apply_bound_method(self):
from typing import Callable, TypeVar

from typing_extensions import ParamSpec, assert_type

P = ParamSpec("P")
T = TypeVar("T")

def apply(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
return func(*args, **kwargs)

class X:
def sample(self, x: int) -> str:
return str(x)

def capybara(x: X) -> None:
assert_type(apply(x.sample, 1), str)
apply(x.sample, "x") # E: incompatible_call

@assert_passes()
def test_apply_any(self):
from typing import Any, Callable, TypeVar

from typing_extensions import ParamSpec, assert_type

P = ParamSpec("P")
T = TypeVar("T")

def apply(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
return func(*args, **kwargs)

def capybara(x, args, kwargs) -> None:
assert_type(apply(x, 1), Any)
assert_type(apply(x.foo, 1), Any)
assert_type(apply(x), Any)
assert_type(apply(x, y=3), Any)
assert_type(apply(x, *args, **kwargs), Any)

@assert_passes()
def test_param_spec_errors(self):
from typing import Callable, TypeVar
Expand Down

0 comments on commit 57fb3fd

Please sign in to comment.