Skip to content
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
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Fix crash on some occurrences of `ParamSpec` in stub files (#797)
- Fix crash when Pydantic 1 is installed (#793)
- Fix error on use of TypeVar defaults in stubs (PEP 696). The
default is still ignored, but now the TypeVar is treated as
Expand Down
8 changes: 5 additions & 3 deletions pyanalyze/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,12 +557,14 @@ def _type_from_runtime(
def make_type_var_value(tv: TypeVarLike, ctx: Context) -> TypeVarValue:
if (
isinstance(tv, (TypeVar, typing_extensions.TypeVar))
and tv.__bound__ is not None
and getattr(tv, "__bound__", None) is not None
):
bound = _type_from_runtime(tv.__bound__, ctx)
else:
bound = None
if isinstance(tv, (TypeVar, typing_extensions.TypeVar)) and tv.__constraints__:
if isinstance(tv, (TypeVar, typing_extensions.TypeVar)) and getattr(
tv, "__constraints__", ()
):
constraints = tuple(
_type_from_runtime(constraint, ctx) for constraint in tv.__constraints__
)
Expand Down Expand Up @@ -1083,7 +1085,7 @@ def visit_Call(self, node: ast.Call) -> Optional[Value]:
else:
return None
return KnownValue(func.val(*args, **kwargs))
elif func.val == TypeVar:
elif is_typing_name(func.val, "TypeVar"):
arg_values = [self.visit(arg) for arg in node.args]
kwarg_values = [(kw.arg, self.visit(kw.value)) for kw in node.keywords]
if not arg_values:
Expand Down
7 changes: 7 additions & 0 deletions pyanalyze/stubs/_pyanalyze_tests-stubs/paramspec.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing_extensions import Callable, ParamSpec, TypeVar

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

def f(x: T) -> T: ...
def g(x: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ...
13 changes: 13 additions & 0 deletions pyanalyze/test_typeshed.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,19 @@ def capybara(x: int):

assert_type(f(x), int)

@assert_passes()
def test_typing_extensions_paramspec(self):
def some_func(x: int) -> str:
return str(x)

def capybara(x: int):
from _pyanalyze_tests.paramspec import f, g
from typing_extensions import assert_type

assert_type(f(x), int)
assert_type(g(some_func, x), str)
g(some_func, "not an int") # TODO should error

def test_typeddict(self):
tsf = TypeshedFinder.make(Checker(), TEST_OPTIONS, verbose=True)
mod = "_pyanalyze_tests.typeddict"
Expand Down