8000 Fix ParamSpec bugs that are blocking typeshed. · google/pytype@76a2ea1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 76a2ea1

Browse files
committed
Fix ParamSpec bugs that are blocking typeshed.
* Don't create multiple TypeVars for the same ParamSpec. We were creating a new TypeVar every time we saw a ParamSpec used in a class definition, leading to "Duplicate top-level identifier" errors. * Show a more helpful error message when P.args or P.kwargs is misspelled. Misspelling a ParamSpec attribute previously produced a mysterious "Can't find pyi for <ParamSpec name>" error at the dependency loading phase. See python/typeshed#6670 and python/typeshed#6676. PiperOrigin-RevId: 419457152
1 parent 375e5ad commit 76a2ea1

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

pytype/pyi/definitions.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import collections
44
import dataclasses
5-
import re
65
import sys
76

87
from typing import Any, Dict, List, Optional, Union
@@ -529,7 +528,8 @@ def _parameterized_type(self, base_type: Any, parameters):
529528
self._matches_full_name(base_type, "typing.Generic")):
530529
# Replacing a ParamSpec with a TypeVar isn't correct, but it'll work
531530
# for simple cases in which the filled value is also a ParamSpec.
532-
self.type_params.append(pytd.TypeParameter(p.name))
531+
if not any(t.name == p.name for t in self.type_params):
532+
self.type_params.append(pytd.TypeParameter(p.name))
533533
processed = p
534534
elif (p in self.param_specs or
535535
(isinstance(p, pytd.GenericType) and
@@ -590,7 +590,10 @@ def new_type(
590590
"""
591591
base_type = self.resolve_type(name)
592592
for p in self.param_specs:
593-
if re.fullmatch(rf"{p.name}\.(args|kwargs)", base_type.name):
593+
if base_type.name.startswith(f"{p.name}."):
594+
_, attr = base_type.name.split(".", 1)
595+
if attr not in ("args", "kwargs"):
596+
raise ParseError(f"Unrecognized ParamSpec attribute: {attr}")
594597
# We do not yet support typing.ParamSpec, so replace references to its
595598
# args and kwargs attributes with Any.
596599
return pytd.AnythingType()

pytype/pyi/parser_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2889,6 +2889,34 @@ def f(x: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ...
28892889
def f(x: Callable[..., T], *args, **kwargs) -> T: ...
28902890
""")
28912891

2892+
def test_paramspec_args_error(self):
2893+
self.check_error("""
2894+
from typing import Any, Callable
2895+
from typing_extensions import ParamSpec
2896+
_P = ParamSpec("_P")
2897+
2898+
class Foo:
2899+
def __init__(
2900+
self, func: Callable[_P, Any], args: _P.args, kwds: _P.kwds
2901+
) -> None: ...
2902+
""", 7, "Unrecognized ParamSpec attribute: kwds")
2903+
2904+
def test_two_classes(self):
2905+
self.check("""
2906+
from typing import Generic, ParamSpec
2907+
P = ParamSpec('P')
2908+
class C1(Generic[P]): ...
2909+
class C2(Generic[P]): ...
2910+
""", """
2911+
from typing import Generic, TypeVar
2912+
2913+
P = TypeVar('P')
2914+
2915+
class C1(Generic[P]): ...
2916+
2917+
class C2(Generic[P]): ...
2918+
""")
2919+
28922920

28932921
class ConcatenateTest(parser_test_base.ParserTestBase):
28942922

0 commit comments

Comments
 (0)
0