5555 'mypy_extensions.KwArg' : ARG_STAR2 ,
5656} # type: Final
5757
58+ GENERIC_STUB_NOT_AT_RUNTIME_TYPES = {
59+ 'queue.Queue' ,
60+ 'builtins._PathLike' ,
61+ } # type: Final
62+
5863
5964def analyze_type_alias (node : Expression ,
6065 api : SemanticAnalyzerCoreInterface ,
@@ -221,8 +226,13 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
221226 res = get_proper_type (res )
222227 if (isinstance (res , Instance ) and len (res .args ) != len (res .type .type_vars ) and
223228 not self .defining_alias ):
224- fix_instance (res , self .fail , disallow_any = disallow_any , use_generic_error = True ,
225- unexpanded_type = t )
229+ fix_instance (
230+ res ,
231+ self .fail ,
232+ self .note ,
233+ disallow_any = disallow_any ,
234+ use_generic_error = True ,
235+ unexpanded_type = t )
226236 return res
227237 elif isinstance (node , TypeInfo ):
228238 return self .analyze_type_with_type_info (node , t .args , t )
@@ -319,13 +329,14 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt
319329
320330 def get_omitted_any (self , typ : Type , fullname : Optional [str ] = None ) -> AnyType :
321331 disallow_any = not self .is_typeshed_stub and self .options .disallow_any_generics
322- return get_omitted_any (disallow_any , self .fail , typ , fullname )
332+ return get_omitted_any (disallow_any , self .fail , self . note , typ , fullname )
323333
324334 def analyze_type_with_type_info (self , info : TypeInfo , args : List [Type ], ctx : Context ) -> Type :
325335 """Bind unbound type when were able to find target TypeInfo.
326336
327337 This handles simple cases like 'int', 'modname.UserClass[str]', etc.
328338 """
339+
329340 if len (args ) > 0 and info .fullname () == 'builtins.tuple' :
330341 fallback = Instance (info , [AnyType (TypeOfAny .special_form )], ctx .line )
331342 return TupleType (self .anal_array (args ), fallback , ctx .line )
@@ -337,7 +348,7 @@ def analyze_type_with_type_info(self, info: TypeInfo, args: List[Type], ctx: Con
337348 instance = Instance (info , self .anal_array (args ), ctx .line , ctx .column )
338349 # Check type argument count.
339350 if len (instance .args ) != len (info .type_vars ) and not self .defining_alias :
340- fix_instance (instance , self .fail ,
351+ fix_instance (instance , self .fail , self . note ,
341352 disallow_any = self .options .disallow_any_generics and
342353 not self .is_typeshed_stub )
343354
@@ -891,10 +902,10 @@ def tuple_type(self, items: List[Type]) -> TupleType:
891902TypeVarList = List [Tuple [str , TypeVarExpr ]]
892903
893904# Mypyc doesn't support callback protocols yet.
894- FailCallback = Callable [[str , Context , DefaultNamedArg (Optional [ErrorCode ], 'code' )], None ]
905+ MsgCallback = Callable [[str , Context , DefaultNamedArg (Optional [ErrorCode ], 'code' )], None ]
895906
896907
897- def get_omitted_any (disallow_any : bool , fail : FailCallback ,
908+ def get_omitted_any (disallow_any : bool , fail : MsgCallback , note : MsgCallback ,
898909 typ : Type , fullname : Optional [str ] = None ,
899910 unexpanded_type : Optional [Type ] = None ) -> AnyType :
900911 if disallow_any :
@@ -907,17 +918,31 @@ def get_omitted_any(disallow_any: bool, fail: FailCallback,
907918 typ = unexpanded_type or typ
908919 type_str = typ .name if isinstance (typ , UnboundType ) else format_type_bare (typ )
909920
910- fail (message_registry .BARE_GENERIC .format (quote_type_string (type_str )), typ ,
911- code = codes .TYPE_ARG )
921+ fail (
922+ message_registry .BARE_GENERIC .format (
923+ quote_type_string (type_str )),
924+ typ ,
925+ code = codes .TYPE_ARG )
926+
927+ if fullname in GENERIC_STUB_NOT_AT_RUNTIME_TYPES :
928+ # Recommend `from __future__ import annotations` or to put type in quotes
929+ # (string literal escaping) for classes not generic at runtime
930+ note (
931+ "Subscripting classes that are not generic at runtime may require "
932+ "escaping, see https://mypy.readthedocs.io/"
933+ "en/latest/common_issues.html#not-generic-runtime" ,
934+ typ ,
935+ code = codes .TYPE_ARG )
936+
912937 any_type = AnyType (TypeOfAny .from_error , line = typ .line , column = typ .column )
913938 else :
914939 any_type = AnyType (TypeOfAny .from_omitted_generics , line = typ .line , column = typ .column )
915940 return any_type
916941
917942
918- def fix_instance (t : Instance , fail : FailCallback ,
943+ def fix_instance (t : Instance , fail : MsgCallback , note : MsgCallback ,
919944 disallow_any : bool , use_generic_error : bool = False ,
920- unexpanded_type : Optional [Type ] = None ) -> None :
945+ unexpanded_type : Optional [Type ] = None , ) -> None :
921946 """Fix a malformed instance by replacing all type arguments with Any.
922947
923948 Also emit a suitable error if this is not due to implicit Any's.
@@ -927,7 +952,7 @@ def fix_instance(t: Instance, fail: FailCallback,
927952 fullname = None # type: Optional[str]
928953 else :
929954 fullname = t .type .fullname ()
930- any_type = get_omitted_any (disallow_any , fail , t , fullname , unexpanded_type )
955+ any_type = get_omitted_any (disallow_any , fail , note , t , fullname , unexpanded_type )
931956 t .args = [any_type ] * len (t .type .type_vars )
932957 return
933958 # Invalid number of type parameters.
@@ -950,7 +975,7 @@ def fix_instance(t: Instance, fail: FailCallback,
950975
951976
952977def expand_type_alias (target : Type , alias_tvars : List [str ], args : List [Type ],
953- fail : FailCallback , no_args : bool , ctx : Context , * ,
978+ fail : MsgCallback , no_args : bool , ctx : Context , * ,
954979 unexpanded_type : Optional [Type ] = None ,
955980 disallow_any : bool = False ) -> Type :
956981 """Expand a (generic) type alias target following the rules outlined in TypeAlias docstring.
@@ -998,7 +1023,7 @@ def set_any_tvars(tp: Type, vars: List[str],
9981023 newline : int , newcolumn : int , * ,
9991024 from_error : bool = False ,
10001025 disallow_any : bool = False ,
1001- fail : Optional [FailCallback ] = None ,
1026+ fail : Optional [MsgCallback ] = None ,
10021027 unexpanded_type : Optional [Type ] = None ) -> ProperType :
10031028 if from_error or disallow_any :
10041029 type_of_any = TypeOfAny .from_error
@@ -1181,20 +1206,21 @@ def make_optional_type(t: Type) -> Type:
11811206 return UnionType ([t , NoneType ()], t .line , t .column )
11821207
11831208
1184- def fix_instance_types (t : Type , fail : FailCallback ) -> None :
1209+ def fix_instance_types (t : Type , fail : MsgCallback , note : MsgCallback ) -> None :
11851210 """Recursively fix all instance types (type argument count) in a given type.
11861211
11871212 For example 'Union[Dict, List[str, int]]' will be transformed into
11881213 'Union[Dict[Any, Any], List[Any]]' in place.
11891214 """
1190- t .accept (InstanceFixer (fail ))
1215+ t .accept (InstanceFixer (fail , note ))
11911216
11921217
11931218class InstanceFixer (TypeTraverserVisitor ):
1194- def __init__ (self , fail : FailCallback ) -> None :
1219+ def __init__ (self , fail : MsgCallback , note : MsgCallback ) -> None :
11951220 self .fail = fail
1221+ self .note = note
11961222
11971223 def visit_instance (self , typ : Instance ) -> None :
11981224 super ().visit_instance (typ )
11991225 if len (typ .args ) != len (typ .type .type_vars ):
1200- fix_instance (typ , self .fail , disallow_any = False , use_generic_error = True )
1226+ fix_instance (typ , self .fail , self . note , disallow_any = False , use_generic_error = True )
0 commit comments