From f48d24947d516ce78187a6cc3dc588e97bb5a751 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 24 Feb 2020 16:32:42 -0800 Subject: [PATCH 01/15] mypy: [minor] use get_type_vars --- mypy/checker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c012251dad9f..d52441c8332e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -50,7 +50,7 @@ erase_def_to_union_or_bound, erase_to_union_or_bound, coerce_to_literal, try_getting_str_literals_from_type, try_getting_int_literals_from_type, tuple_fallback, is_singleton_type, try_expanding_enum_to_union, - true_only, false_only, function_type, TypeVarExtractor, custom_special_method, + true_only, false_only, function_type, get_type_vars, custom_special_method, is_literal_type_like, ) from mypy import message_registry @@ -5328,7 +5328,7 @@ def detach_callable(typ: CallableType) -> CallableType: appear_map = {} # type: Dict[str, List[int]] for i, inner_type in enumerate(type_list): - typevars_available = inner_type.accept(TypeVarExtractor()) + typevars_available = get_type_vars(inner_type) for var in typevars_available: if var.fullname not in appear_map: appear_map[var.fullname] = [] @@ -5338,7 +5338,7 @@ def detach_callable(typ: CallableType) -> CallableType: for var_name, appearances in appear_map.items(): used_type_var_names.add(var_name) - all_type_vars = typ.accept(TypeVarExtractor()) + all_type_vars = get_type_vars(typ) new_variables = [] for var in set(all_type_vars): if var.fullname not in used_type_var_names: From 1597663912e850f6687edfed74bab90fe3709a60 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 24 Feb 2020 22:23:02 -0800 Subject: [PATCH 02/15] semanal: make some typevar functions reusable for paramspec --- mypy/semanal.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index c3b9f8e91817..f96ff9b51973 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2823,7 +2823,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: Return True if this looks like a type variable declaration (but maybe with errors), otherwise return False. """ - call = self.get_typevar_declaration(s) + call = self.get_typevarlike_declaration(s, "typing.TypeVar") if not call: return False @@ -2834,7 +2834,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: return False name = lvalue.name - if not self.check_typevar_name(call, name, s): + if not self.check_typevarlike_name(call, name, s): return False # Constraining types @@ -2894,24 +2894,31 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: self.add_symbol(name, call.analyzed, s) return True - def check_typevar_name(self, call: CallExpr, name: str, context: Context) -> bool: + def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> bool: + """Checks that the name of a TypeVar or ParamSpec matches its variable.""" name = unmangle(name) + assert isinstance(call.callee, RefExpr) + typevarlike_type = ( + call.callee.name if isinstance(call.callee, NameExpr) else call.callee.fullname + ) if len(call.args) < 1: - self.fail("Too few arguments for TypeVar()", context) + self.fail("Too few arguments for {}()".format(typevarlike_type), context) return False if (not isinstance(call.args[0], (StrExpr, BytesExpr, UnicodeExpr)) or not call.arg_kinds[0] == ARG_POS): - self.fail("TypeVar() expects a string literal as first argument", context) + self.fail("{}() expects a string literal as first argument".format(typevarlike_type), + context) return False elif call.args[0].value != name: - msg = "String argument 1 '{}' to TypeVar(...) does not match variable name '{}'" - self.fail(msg.format(call.args[0].value, name), context) + msg = "String argument 1 '{}' to {}(...) does not match variable name '{}'" + self.fail(msg.format(call.args[0].value, typevarlike_type, name), context) return False return True - def get_typevar_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]: - """Returns the TypeVar() call expression if `s` is a type var declaration - or None otherwise. + def get_typevarlike_declaration(self, s: AssignmentStmt, + typevarlike_type: str) -> Optional[CallExpr]: + """Returns the call expression if `s` is a declaration of `typevarlike_type` + (TypeVar or ParamSpec), or None otherwise. """ if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr): return None @@ -2921,7 +2928,7 @@ def get_typevar_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]: callee = call.callee if not isinstance(callee, RefExpr): return None - if callee.fullname != 'typing.TypeVar': + if callee.fullname != typevarlike_type: return None return call From 2c1d5ec1edca9e75ebb2cc3fbbafb440f816d324 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 22 Aug 2020 13:09:41 -0700 Subject: [PATCH 03/15] tests: add fixture for typing_extensions.ParamSpec --- test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 9197866e4a4e..946430d106a6 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -21,6 +21,8 @@ Literal: _SpecialForm = ... Annotated: _SpecialForm = ... +ParamSpec: _SpecialForm +Concatenate: _SpecialForm # Fallback type for all typed dicts (does not exist at runtime). class _TypedDict(Mapping[str, object]): From ae8b9f868ba61ce8e04b91db5db169146dd79b79 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 24 Feb 2020 22:32:03 -0800 Subject: [PATCH 04/15] semanal: add test for ParamSpec --- test-data/unit/semanal-errors.test | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index afd39122f99e..e093f3fd1a0a 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1419,3 +1419,12 @@ def g() -> None: # N: (Hint: Use "T" in function signature to bind "T" inside a function) [builtins fixtures/dict.pyi] [out] + +[case testParamSpec] +from typing_extensions import ParamSpec + +TParams = ParamSpec('TParams') +TP = ParamSpec('?') # E: String argument 1 '?' to ParamSpec(...) does not match variable name 'TP' +TP2: int = ParamSpec('TP2') # E: Cannot declare the type of a parameter specification + +[out] From 4a71f2730e3982544c91bed1e5784500b3890fb6 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 24 Feb 2020 22:23:52 -0800 Subject: [PATCH 05/15] semanal: process ParamSpecs --- mypy/semanal.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index f96ff9b51973..d222fc4bb6be 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -76,6 +76,7 @@ nongen_builtins, get_member_expr_fullname, REVEAL_TYPE, REVEAL_LOCALS, is_final_node, TypedDictExpr, type_aliases_target_versions, EnumCallExpr, RUNTIME_PROTOCOL_DECOS, FakeExpression, Statement, AssignmentExpr, + ParamSpecExpr ) from mypy.tvar_scope import TypeVarScope from mypy.typevars import fill_typevars @@ -1921,6 +1922,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: # * type variable definition elif self.process_typevar_declaration(s): special_form = True + elif self.process_paramspec_declaration(s): + special_form = True # * type constructors elif self.analyze_namedtuple_assign(s): special_form = True @@ -3015,6 +3018,41 @@ def process_typevar_parameters(self, args: List[Expression], variance = INVARIANT return variance, upper_bound + def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: + """Checks if s declares a ParamSpec; if yes, store it in symbol table. + + Return True if this looks like a parameter specification declaration (but maybe + with errors), otherwise return False. + + In the future, ParamSpec may accept bounds and variance arguments, in which + case more aggressive sharing of code with process_typevar_declaration should be pursued. + + """ + call = self.get_typevarlike_declaration(s, "typing_extensions.ParamSpec") + if not call: + return False + + lvalue = s.lvalues[0] + assert isinstance(lvalue, NameExpr) + if s.type: + self.fail("Cannot declare the type of a parameter specification", s) + return False + + name = lvalue.name + if not self.check_typevarlike_name(call, name, s): + return False + + # PEP 612 reserves the right to define bound, covariant and contravariant arguments to + # ParamSpec in a later PEP. If and when that happens, we should do something + # on the lines of process_typevar_parameters + paramspec_var = ParamSpecExpr( + name, self.qualified_name(name), self.object_type(), INVARIANT + ) + paramspec_var.line = call.line + call.analyzed = paramspec_var + self.add_symbol(name, call.analyzed, s) + return True + def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance) -> TypeInfo: class_def = ClassDef(name, Block([])) if self.is_func_scope() and not self.type: From 94a96f89b97e5ebdd017ae348e7f28037da5d1da Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 6 Apr 2020 17:37:47 -0700 Subject: [PATCH 06/15] nodes: add node for ParamSpec --- mypy/nodes.py | 72 +++++++++++++++++++++++++++++++++++-------------- mypy/visitor.py | 7 +++++ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 8ccb522323ba..53630c8d9174 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2043,23 +2043,10 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: CONTRAVARIANT = 2 # type: Final[int] -class TypeVarExpr(SymbolNode, Expression): - """Type variable expression TypeVar(...). - - This is also used to represent type variables in symbol tables. - - A type variable is not valid as a type unless bound in a TypeVarScope. - That happens within: - - 1. a generic class that uses the type variable as a type argument or - 2. a generic function that refers to the type variable in its signature. - """ - +class TypeVarLikeExpr(SymbolNode, Expression): + """Base class for TypeVarExpr and ParamSpecExpr.""" _name = '' _fullname = '' - # Value restriction: only types in the list are valid as values. If the - # list is empty, there is no restriction. - values = None # type: List[mypy.types.Type] # Upper bound: only subtypes of upper_bound are valid as values. By default # this is 'object', meaning no restriction. upper_bound = None # type: mypy.types.Type @@ -2069,14 +2056,12 @@ class TypeVarExpr(SymbolNode, Expression): # variable. variance = INVARIANT - def __init__(self, name: str, fullname: str, - values: List['mypy.types.Type'], - upper_bound: 'mypy.types.Type', - variance: int = INVARIANT) -> None: + def __init__( + self, name: str, fullname: str, upper_bound: 'mypy.types.Type', variance: int = INVARIANT + ) -> None: super().__init__() self._name = name self._fullname = fullname - self.values = values self.upper_bound = upper_bound self.variance = variance @@ -2088,6 +2073,29 @@ def name(self) -> str: def fullname(self) -> str: return self._fullname + +class TypeVarExpr(TypeVarLikeExpr): + """Type variable expression TypeVar(...). + + This is also used to represent type variables in symbol tables. + + A type variable is not valid as a type unless bound in a TypeVarScope. + That happens within: + + 1. a generic class that uses the type variable as a type argument or + 2. a generic function that refers to the type variable in its signature. + """ + # Value restriction: only types in the list are valid as values. If the + # list is empty, there is no restriction. + values = None # type: List[mypy.types.Type] + + def __init__(self, name: str, fullname: str, + values: List['mypy.types.Type'], + upper_bound: 'mypy.types.Type', + variance: int = INVARIANT) -> None: + super().__init__(name, fullname, upper_bound, variance) + self.values = values + def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_type_var_expr(self) @@ -2110,6 +2118,30 @@ def deserialize(cls, data: JsonDict) -> 'TypeVarExpr': data['variance']) +class ParamSpecExpr(TypeVarLikeExpr): + def accept(self, visitor: ExpressionVisitor[T]) -> T: + return visitor.visit_paramspec_var_expr(self) + + def serialize(self) -> JsonDict: + return { + '.class': 'ParamSpecExpr', + 'name': self._name, + 'fullname': self._fullname, + 'upper_bound': self.upper_bound.serialize(), + 'variance': self.variance, + } + + @classmethod + def deserialize(cls, data: JsonDict) -> 'ParamSpecExpr': + assert data['.class'] == 'ParamSpecExpr' + return ParamSpecExpr( + data['name'], + data['fullname'], + mypy.types.deserialize_type(data['upper_bound']), + data['variance'] + ) + + class TypeAliasExpr(Expression): """Type alias expression (rvalue).""" diff --git a/mypy/visitor.py b/mypy/visitor.py index d692142e6bcc..0093c02ca44c 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -155,6 +155,10 @@ def visit_backquote_expr(self, o: 'mypy.nodes.BackquoteExpr') -> T: def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: pass + @abstractmethod + def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: + pass + @abstractmethod def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> T: pass @@ -529,6 +533,9 @@ def visit_backquote_expr(self, o: 'mypy.nodes.BackquoteExpr') -> T: def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: pass + def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: + pass + def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> T: pass From dd9ba262e081b46932459a9a064030931da8bf30 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 6 Apr 2020 20:24:54 -0700 Subject: [PATCH 07/15] checkexpr: add paramspec visitor to ExpressionChecker This allows checkexpr to recognise the call to typing.ParamSpec as producing a special form --- mypy/checkexpr.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index aa371548127e..fb4da10a46ed 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -31,6 +31,7 @@ DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr, YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr, TypeAliasExpr, BackquoteExpr, EnumCallExpr, TypeAlias, SymbolNode, PlaceholderNode, + ParamSpecExpr, ARG_POS, ARG_OPT, ARG_NAMED, ARG_STAR, ARG_STAR2, LITERAL_TYPE, REVEAL_TYPE, ) from mypy.literals import literal @@ -3973,6 +3974,9 @@ def visit_temp_node(self, e: TempNode) -> Type: def visit_type_var_expr(self, e: TypeVarExpr) -> Type: return AnyType(TypeOfAny.special_form) + def visit_paramspec_var_expr(self, e: ParamSpecExpr) -> Type: + return AnyType(TypeOfAny.special_form) + def visit_newtype_expr(self, e: NewTypeExpr) -> Type: return AnyType(TypeOfAny.special_form) From da33b280be9feea4250d608b24808f881130a939 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 1 Aug 2020 20:08:55 -0700 Subject: [PATCH 08/15] literals: [minor] add a useless visitor method --- mypy/literals.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/literals.py b/mypy/literals.py index 4779abf871c9..76b5c406da62 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -8,7 +8,7 @@ ConditionalExpr, EllipsisExpr, YieldFromExpr, YieldExpr, RevealExpr, SuperExpr, TypeApplication, LambdaExpr, ListComprehension, SetComprehension, DictionaryComprehension, GeneratorExpr, BackquoteExpr, TypeVarExpr, TypeAliasExpr, NamedTupleExpr, EnumCallExpr, - TypedDictExpr, NewTypeExpr, PromoteExpr, AwaitExpr, TempNode, AssignmentExpr, + TypedDictExpr, NewTypeExpr, PromoteExpr, AwaitExpr, TempNode, AssignmentExpr, ParamSpecExpr ) from mypy.visitor import ExpressionVisitor @@ -213,6 +213,9 @@ def visit_backquote_expr(self, e: BackquoteExpr) -> None: def visit_type_var_expr(self, e: TypeVarExpr) -> None: return None + def visit_paramspec_var_expr(self, e: ParamSpecExpr) -> None: + return None + def visit_type_alias_expr(self, e: TypeAliasExpr) -> None: return None From 6db7f684a625d19dd8bf8cf9fe87afd2cb6b29d4 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 22 Aug 2020 13:21:51 -0700 Subject: [PATCH 09/15] [mypyc] add a useless visitor to IRBuilderVisitor --- mypyc/irbuild/visitor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 0fd9dce3f737..1e0df5018799 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -16,7 +16,7 @@ FloatExpr, GeneratorExpr, GlobalDecl, LambdaExpr, ListComprehension, SetComprehension, NamedTupleExpr, NewTypeExpr, NonlocalDecl, OverloadedFuncDef, PrintStmt, RaiseStmt, RevealExpr, SetExpr, SliceExpr, StarExpr, SuperExpr, TryStmt, TypeAliasExpr, TypeApplication, - TypeVarExpr, TypedDictExpr, UnicodeExpr, WithStmt, YieldFromExpr, YieldExpr + TypeVarExpr, TypedDictExpr, UnicodeExpr, WithStmt, YieldFromExpr, YieldExpr, ParamSpecExpr ) from mypyc.ir.ops import Value @@ -309,6 +309,9 @@ def visit_type_application(self, o: TypeApplication) -> Value: def visit_type_var_expr(self, o: TypeVarExpr) -> Value: assert False, "can't compile analysis-only expressions" + def visit_paramspec_var_expr(self, o: ParamSpecExpr) -> Value: + assert False, "can't compile analysis-only expressions" + def visit_typeddict_expr(self, o: TypedDictExpr) -> Value: assert False, "can't compile analysis-only expressions" From 7b6681ea281a5ffe6207dd31a06d529e753843ab Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:24:58 -0700 Subject: [PATCH 10/15] semanal: [minor] fix docstring nit --- mypy/semanal.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index d222fc4bb6be..6fc6bc12774b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3021,12 +3021,10 @@ def process_typevar_parameters(self, args: List[Expression], def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: """Checks if s declares a ParamSpec; if yes, store it in symbol table. - Return True if this looks like a parameter specification declaration (but maybe - with errors), otherwise return False. + Return True if this looks like a ParamSpec (maybe with errors), otherwise return False. In the future, ParamSpec may accept bounds and variance arguments, in which case more aggressive sharing of code with process_typevar_declaration should be pursued. - """ call = self.get_typevarlike_declaration(s, "typing_extensions.ParamSpec") if not call: From bfc0ab9be337cd17e385891e708a80e7f12ed5ce Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:42:15 -0700 Subject: [PATCH 11/15] semanal: recognise typing.ParamSpec as well --- mypy/semanal.py | 10 ++++++---- test-data/unit/lib-stub/typing.pyi | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6fc6bc12774b..b640ab23815e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2826,7 +2826,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: Return True if this looks like a type variable declaration (but maybe with errors), otherwise return False. """ - call = self.get_typevarlike_declaration(s, "typing.TypeVar") + call = self.get_typevarlike_declaration(s, ("typing.TypeVar",)) if not call: return False @@ -2919,7 +2919,7 @@ def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> return True def get_typevarlike_declaration(self, s: AssignmentStmt, - typevarlike_type: str) -> Optional[CallExpr]: + typevarlike_types: Tuple[str, ...]) -> Optional[CallExpr]: """Returns the call expression if `s` is a declaration of `typevarlike_type` (TypeVar or ParamSpec), or None otherwise. """ @@ -2931,7 +2931,7 @@ def get_typevarlike_declaration(self, s: AssignmentStmt, callee = call.callee if not isinstance(callee, RefExpr): return None - if callee.fullname != typevarlike_type: + if callee.fullname not in typevarlike_types: return None return call @@ -3026,7 +3026,9 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: In the future, ParamSpec may accept bounds and variance arguments, in which case more aggressive sharing of code with process_typevar_declaration should be pursued. """ - call = self.get_typevarlike_declaration(s, "typing_extensions.ParamSpec") + call = self.get_typevarlike_declaration( + s, ("typing_extensions.ParamSpec", "typing.ParamSpec") + ) if not call: return False diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index 3d403b1845db..2f42633843e0 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -24,6 +24,7 @@ ClassVar = 0 Final = 0 NoReturn = 0 NewType = 0 +ParamSpec = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) From a38745eaa8bd89de2748b9f84aaee547cbf4bd62 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:42:41 -0700 Subject: [PATCH 12/15] strconv, treetransform: add param spec visitor --- mypy/strconv.py | 11 +++++++++++ mypy/treetransform.py | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mypy/strconv.py b/mypy/strconv.py index 533bf4f390ba..b856db49105f 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -467,6 +467,17 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> str: a += ['UpperBound({})'.format(o.upper_bound)] return self.dump(a, o) + def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: + import mypy.types + a = [] # type: List[Any] + if o.variance == mypy.nodes.COVARIANT: + a += ['Variance(COVARIANT)'] + if o.variance == mypy.nodes.CONTRAVARIANT: + a += ['Variance(CONTRAVARIANT)'] + if not mypy.types.is_named_instance(o.upper_bound, 'builtins.object'): + a += ['UpperBound({})'.format(o.upper_bound)] + return self.dump(a, o) + def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> str: return 'TypeAliasExpr({})'.format(o.type) diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 06339a2a859a..afbdf301a1b4 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -15,7 +15,7 @@ ConditionalExpr, DictExpr, SetExpr, NameExpr, IntExpr, StrExpr, BytesExpr, UnicodeExpr, FloatExpr, CallExpr, SuperExpr, MemberExpr, IndexExpr, SliceExpr, OpExpr, UnaryExpr, LambdaExpr, TypeApplication, PrintStmt, - SymbolTable, RefExpr, TypeVarExpr, NewTypeExpr, PromoteExpr, + SymbolTable, RefExpr, TypeVarExpr, ParamSpecExpr, NewTypeExpr, PromoteExpr, ComparisonExpr, TempNode, StarExpr, Statement, Expression, YieldFromExpr, NamedTupleExpr, TypedDictExpr, NonlocalDecl, SetComprehension, DictionaryComprehension, ComplexExpr, TypeAliasExpr, EllipsisExpr, @@ -498,6 +498,11 @@ def visit_type_var_expr(self, node: TypeVarExpr) -> TypeVarExpr: self.types(node.values), self.type(node.upper_bound), variance=node.variance) + def visit_paramspec_var_expr(self, node: ParamSpecExpr) -> ParamSpecExpr: + return ParamSpecExpr( + node.name, node.fullname, self.type(node.upper_bound), variance=node.variance + ) + def visit_type_alias_expr(self, node: TypeAliasExpr) -> TypeAliasExpr: return TypeAliasExpr(node.node) From 9c2c41998ee7ae287db14af9aafa4a6c6413706a Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:43:30 -0700 Subject: [PATCH 13/15] semanal-types: [minor] fix typo --- test-data/unit/semanal-types.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 64b2110db4d6..87cc757b26db 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1482,7 +1482,7 @@ def f(x: (int, int)) -> None: pass main:1: error: Syntax error in type annotation main:1: note: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) -[case tesQualifiedTypeNameBasedOnAny] +[case testQualifiedTypeNameBasedOnAny] from typing import Any x = 0 # type: Any z = 0 # type: x.y From 5dd28ed76a7358b322ca042545ff39da5e5549fd Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:43:43 -0700 Subject: [PATCH 14/15] semanal-types: add testParamSpec --- test-data/unit/semanal-types.test | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 87cc757b26db..28f8ee22f848 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1497,3 +1497,14 @@ MypyFile:1( NameExpr(z [__main__.z]) IntExpr(0) Any)) + + +[case testParamSpec] +from typing import ParamSpec +P = ParamSpec("P") +[out] +MypyFile:1( + ImportFrom:1(typing, [ParamSpec]) + AssignmentStmt:2( + NameExpr(P* [__main__.P]) + ParamSpecExpr:2())) From 7b9a0529ebcc469be40d035bb0c1aad17a24a2ae Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sat, 29 Aug 2020 14:54:15 -0700 Subject: [PATCH 15/15] [minor] rename visitor method for better consistency --- mypy/checkexpr.py | 2 +- mypy/literals.py | 2 +- mypy/nodes.py | 2 +- mypy/strconv.py | 2 +- mypy/treetransform.py | 2 +- mypy/visitor.py | 4 ++-- mypyc/irbuild/visitor.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fb4da10a46ed..e6cde8cf370b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3974,7 +3974,7 @@ def visit_temp_node(self, e: TempNode) -> Type: def visit_type_var_expr(self, e: TypeVarExpr) -> Type: return AnyType(TypeOfAny.special_form) - def visit_paramspec_var_expr(self, e: ParamSpecExpr) -> Type: + def visit_paramspec_expr(self, e: ParamSpecExpr) -> Type: return AnyType(TypeOfAny.special_form) def visit_newtype_expr(self, e: NewTypeExpr) -> Type: diff --git a/mypy/literals.py b/mypy/literals.py index 76b5c406da62..95872cbd9fca 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -213,7 +213,7 @@ def visit_backquote_expr(self, e: BackquoteExpr) -> None: def visit_type_var_expr(self, e: TypeVarExpr) -> None: return None - def visit_paramspec_var_expr(self, e: ParamSpecExpr) -> None: + def visit_paramspec_expr(self, e: ParamSpecExpr) -> None: return None def visit_type_alias_expr(self, e: TypeAliasExpr) -> None: diff --git a/mypy/nodes.py b/mypy/nodes.py index 53630c8d9174..9af5dc5f75cc 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2120,7 +2120,7 @@ def deserialize(cls, data: JsonDict) -> 'TypeVarExpr': class ParamSpecExpr(TypeVarLikeExpr): def accept(self, visitor: ExpressionVisitor[T]) -> T: - return visitor.visit_paramspec_var_expr(self) + return visitor.visit_paramspec_expr(self) def serialize(self) -> JsonDict: return { diff --git a/mypy/strconv.py b/mypy/strconv.py index b856db49105f..50918dab0308 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -467,7 +467,7 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> str: a += ['UpperBound({})'.format(o.upper_bound)] return self.dump(a, o) - def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: + def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: import mypy.types a = [] # type: List[Any] if o.variance == mypy.nodes.COVARIANT: diff --git a/mypy/treetransform.py b/mypy/treetransform.py index afbdf301a1b4..4191569995b0 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -498,7 +498,7 @@ def visit_type_var_expr(self, node: TypeVarExpr) -> TypeVarExpr: self.types(node.values), self.type(node.upper_bound), variance=node.variance) - def visit_paramspec_var_expr(self, node: ParamSpecExpr) -> ParamSpecExpr: + def visit_paramspec_expr(self, node: ParamSpecExpr) -> ParamSpecExpr: return ParamSpecExpr( node.name, node.fullname, self.type(node.upper_bound), variance=node.variance ) diff --git a/mypy/visitor.py b/mypy/visitor.py index 0093c02ca44c..09a6cea9106a 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -156,7 +156,7 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: pass @abstractmethod - def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: + def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: pass @abstractmethod @@ -533,7 +533,7 @@ def visit_backquote_expr(self, o: 'mypy.nodes.BackquoteExpr') -> T: def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: pass - def visit_paramspec_var_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: + def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: pass def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> T: diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 1e0df5018799..cd6dec8890b3 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -309,7 +309,7 @@ def visit_type_application(self, o: TypeApplication) -> Value: def visit_type_var_expr(self, o: TypeVarExpr) -> Value: assert False, "can't compile analysis-only expressions" - def visit_paramspec_var_expr(self, o: ParamSpecExpr) -> Value: + def visit_paramspec_expr(self, o: ParamSpecExpr) -> Value: assert False, "can't compile analysis-only expressions" def visit_typeddict_expr(self, o: TypedDictExpr) -> Value: