From be6adaeeeb592f2249cb33c533fb708ca260c2bb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 23 Aug 2022 03:03:50 +0100 Subject: [PATCH] Fix daemon crashes related to ParamSpec and TypeVarTuple (#13381) * Fix daemon crashes related to ParamSpec and TypeVarTuple Fix daemon crash when using fine-grained caching and ParamSpec, with traceback like this (when using a compiled mypy): ``` Traceback (most recent call last): File "mypy/dmypy_server.py", line 230, in serve File "mypy/dmypy_server.py", line 273, in run_command File "mypy/dmypy_server.py", line 372, in cmd_recheck File "mypy/dmypy_server.py", line 529, in fine_grained_increment File "mypy/server/update.py", line 245, in update File "mypy/server/update.py", line 328, in update_one File "mypy/server/update.py", line 387, in update_module File "mypy/server/astdiff.py", line 158, in snapshot_symbol_table File "mypy/server/astdiff.py", line 236, in snapshot_type File "mypy/types.py", line 1173, in accept File "mypy/server/astdiff.py", line 300, in visit_instance File "mypy/nodes.py", line 2764, in fullname AttributeError: attribute 'TypeInfo' of '_fullname' undefined ``` Also fix TypeVarTuple crashes when using daemon. Co-authored-by: Ivan Levkivskyi --- mypy/fixup.py | 8 +++ mypy/server/astdiff.py | 3 ++ mypy/test/testfinegrained.py | 1 + test-data/unit/fine-grained.test | 83 ++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/mypy/fixup.py b/mypy/fixup.py index 7f7c3129005c..b3a2d43d6b4d 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -13,10 +13,12 @@ FuncDef, MypyFile, OverloadedFuncDef, + ParamSpecExpr, SymbolTable, TypeAlias, TypeInfo, TypeVarExpr, + TypeVarTupleExpr, Var, ) from mypy.types import ( @@ -164,6 +166,12 @@ def visit_type_var_expr(self, tv: TypeVarExpr) -> None: value.accept(self.type_fixer) tv.upper_bound.accept(self.type_fixer) + def visit_paramspec_expr(self, p: ParamSpecExpr) -> None: + p.upper_bound.accept(self.type_fixer) + + def visit_type_var_tuple_expr(self, tv: TypeVarTupleExpr) -> None: + tv.upper_bound.accept(self.type_fixer) + def visit_var(self, v: Var) -> None: if self.current_info is not None: v.info = self.current_info diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index e913188df02f..37e195f5e0b1 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -68,6 +68,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' TypeAlias, TypeInfo, TypeVarExpr, + TypeVarTupleExpr, Var, ) from mypy.types import ( @@ -189,6 +190,8 @@ def snapshot_symbol_table(name_prefix: str, table: SymbolTable) -> dict[str, Sna ) elif isinstance(node, ParamSpecExpr): result[name] = ("ParamSpec", node.variance, snapshot_type(node.upper_bound)) + elif isinstance(node, TypeVarTupleExpr): + result[name] = ("TypeVarTuple", node.variance, snapshot_type(node.upper_bound)) else: assert symbol.kind != UNBOUND_IMPORTED if node and get_prefix(node.fullname) != name_prefix: diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 1cc8ba6198d1..bd5628799c8b 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -151,6 +151,7 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo options.use_fine_grained_cache = self.use_cache and not build_cache options.cache_fine_grained = self.use_cache options.local_partial_types = True + options.enable_incomplete_features = True if re.search("flags:.*--follow-imports", source) is None: # Override the default for follow_imports options.follow_imports = "error" diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 8ef04562abbf..3a054e8fcfe5 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9819,6 +9819,89 @@ x: str [out] == +[case testParamSpecCached] +import a + +[file a.py] +import b + +def f(x: int) -> str: return 'x' + +b.foo(f) + +[file a.py.2] +import b + +def f(x: int) -> str: return 'x' + +reveal_type(b.foo(f)) + +[file b.py] +from typing import TypeVar, Callable, Union +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") + +def foo(f: Callable[P, T]) -> Callable[P, Union[T, None]]: + return f + +[file b.py.2] +from typing import TypeVar, Callable, Union +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") + +def foo(f: Callable[P, T]) -> Callable[P, Union[T, None]]: + return f + +x = 0 # Arbitrary change to trigger reprocessing + +[builtins fixtures/dict.pyi] +[out] +== +a.py:5: note: Revealed type is "def (x: builtins.int) -> builtins.str" + +[case testTypeVarTupleCached] +import a + +[file a.py] +import b + +def f(x: int) -> str: return 'x' + +b.foo((1, 'x')) + +[file a.py.2] +import b + +reveal_type(b.foo((1, 'x'))) + +[file b.py] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") + +def foo(t: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + return t + +[file b.py.2] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") + +def foo(t: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + return t + +x = 0 # Arbitrary change to trigger reprocessing +[builtins fixtures/dict.pyi] +[out] +== +a.py:3: note: Revealed type is "Tuple[Literal[1]?, Literal['x']?]" + [case testUnpackKwargsUpdateFine] # flags: --enable-incomplete-features import m