2828 If ,
2929 Import ,
3030 ImportFrom ,
31- Index ,
3231 List ,
3332 Load ,
3433 LShift ,
@@ -389,9 +388,7 @@ def visit_BinOp(self, node: BinOp) -> Any:
389388 union_name = self .transformer ._get_import ("typing" , "Union" )
390389 return Subscript (
391390 value = union_name ,
392- slice = Index (
393- Tuple (elts = [node .left , node .right ], ctx = Load ()), ctx = Load ()
394- ),
391+ slice = Tuple (elts = [node .left , node .right ], ctx = Load ()),
395392 ctx = Load (),
396393 )
397394
@@ -410,24 +407,18 @@ def visit_Subscript(self, node: Subscript) -> Any:
410407 # The subscript of typing(_extensions).Literal can be any arbitrary string, so
411408 # don't try to evaluate it as code
412409 if node .slice :
413- if isinstance (node .slice , Index ):
414- # Python 3.8
415- slice_value = node .slice .value # type: ignore[attr-defined]
416- else :
417- slice_value = node .slice
418-
419- if isinstance (slice_value , Tuple ):
410+ if isinstance (node .slice , Tuple ):
420411 if self ._memo .name_matches (node .value , * annotated_names ):
421412 # Only treat the first argument to typing.Annotated as a potential
422413 # forward reference
423414 items = cast (
424415 typing .List [expr ],
425- [self .visit (slice_value . elts [0 ])] + slice_value .elts [1 :],
416+ [self .visit (node . slice . elts [0 ])] + node . slice .elts [1 :],
426417 )
427418 else :
428419 items = cast (
429420 typing .List [expr ],
430- [self .visit (item ) for item in slice_value .elts ],
421+ [self .visit (item ) for item in node . slice .elts ],
431422 )
432423
433424 # If this is a Union and any of the items is Any, erase the entire
@@ -450,7 +441,7 @@ def visit_Subscript(self, node: Subscript) -> Any:
450441 if item is None :
451442 items [index ] = self .transformer ._get_import ("typing" , "Any" )
452443
453- slice_value .elts = items
444+ node . slice .elts = items
454445 else :
455446 self .generic_visit (node )
456447
@@ -542,18 +533,10 @@ def _use_memo(
542533 return_annotation , * generator_names
543534 ):
544535 if isinstance (return_annotation , Subscript ):
545- annotation_slice = return_annotation .slice
546-
547- # Python < 3.9
548- if isinstance (annotation_slice , Index ):
549- annotation_slice = (
550- annotation_slice .value # type: ignore[attr-defined]
551- )
552-
553- if isinstance (annotation_slice , Tuple ):
554- items = annotation_slice .elts
536+ if isinstance (return_annotation .slice , Tuple ):
537+ items = return_annotation .slice .elts
555538 else :
556- items = [annotation_slice ]
539+ items = [return_annotation . slice ]
557540
558541 if len (items ) > 0 :
559542 new_memo .yield_annotation = self ._convert_annotation (
@@ -743,7 +726,7 @@ def visit_FunctionDef(
743726 annotation_ = self ._convert_annotation (node .args .vararg .annotation )
744727 if annotation_ :
745728 container = Name ("tuple" , ctx = Load ())
746- subscript_slice : Tuple | Index = Tuple (
729+ subscript_slice = Tuple (
747730 [
748731 annotation_ ,
749732 Constant (Ellipsis ),
@@ -1024,12 +1007,25 @@ def visit_AnnAssign(self, node: AnnAssign) -> Any:
10241007 func_name = self ._get_import (
10251008 "typeguard._functions" , "check_variable_assignment"
10261009 )
1010+ targets_arg = List (
1011+ [
1012+ List (
1013+ [
1014+ Tuple (
1015+ [Constant (node .target .id ), annotation ],
1016+ ctx = Load (),
1017+ )
1018+ ],
1019+ ctx = Load (),
1020+ )
1021+ ],
1022+ ctx = Load (),
1023+ )
10271024 node .value = Call (
10281025 func_name ,
10291026 [
10301027 node .value ,
1031- Constant (node .target .id ),
1032- annotation ,
1028+ targets_arg ,
10331029 self ._memo .get_memo_name (),
10341030 ],
10351031 [],
@@ -1047,7 +1043,7 @@ def visit_Assign(self, node: Assign) -> Any:
10471043
10481044 # Only instrument function-local assignments
10491045 if isinstance (self ._memo .node , (FunctionDef , AsyncFunctionDef )):
1050- targets : list [dict [ Constant , expr | None ]] = []
1046+ preliminary_targets : list [list [ tuple [ Constant , expr | None ] ]] = []
10511047 check_required = False
10521048 for target in node .targets :
10531049 elts : Sequence [expr ]
@@ -1058,63 +1054,63 @@ def visit_Assign(self, node: Assign) -> Any:
10581054 else :
10591055 continue
10601056
1061- annotations_ : dict [ Constant , expr | None ] = {}
1057+ annotations_ : list [ tuple [ Constant , expr | None ]] = []
10621058 for exp in elts :
10631059 prefix = ""
10641060 if isinstance (exp , Starred ):
10651061 exp = exp .value
10661062 prefix = "*"
10671063
1064+ path : list [str ] = []
1065+ while isinstance (exp , Attribute ):
1066+ path .insert (0 , exp .attr )
1067+ exp = exp .value
1068+
10681069 if isinstance (exp , Name ):
1069- self ._memo .ignored_names .add (exp .id )
1070- name = prefix + exp .id
1070+ if not path :
1071+ self ._memo .ignored_names .add (exp .id )
1072+
1073+ path .insert (0 , exp .id )
1074+ name = prefix + "." .join (path )
10711075 annotation = self ._memo .variable_annotations .get (exp .id )
10721076 if annotation :
1073- annotations_ [ Constant (name )] = annotation
1077+ annotations_ . append (( Constant (name ), annotation ))
10741078 check_required = True
10751079 else :
1076- annotations_ [ Constant (name )] = None
1080+ annotations_ . append (( Constant (name ), None ))
10771081
1078- targets .append (annotations_ )
1082+ preliminary_targets .append (annotations_ )
10791083
10801084 if check_required :
10811085 # Replace missing annotations with typing.Any
1082- for item in targets :
1083- for key , expression in item .items ():
1086+ targets : list [list [tuple [Constant , expr ]]] = []
1087+ for items in preliminary_targets :
1088+ target_list : list [tuple [Constant , expr ]] = []
1089+ targets .append (target_list )
1090+ for key , expression in items :
10841091 if expression is None :
1085- item [key ] = self ._get_import ("typing" , "Any" )
1092+ target_list .append ((key , self ._get_import ("typing" , "Any" )))
1093+ else :
1094+ target_list .append ((key , expression ))
10861095
1087- if len (targets ) == 1 and len (targets [0 ]) == 1 :
1088- func_name = self ._get_import (
1089- "typeguard._functions" , "check_variable_assignment"
1090- )
1091- target_varname = next (iter (targets [0 ]))
1092- node .value = Call (
1093- func_name ,
1094- [
1095- node .value ,
1096- target_varname ,
1097- targets [0 ][target_varname ],
1098- self ._memo .get_memo_name (),
1099- ],
1100- [],
1101- )
1102- elif targets :
1103- func_name = self ._get_import (
1104- "typeguard._functions" , "check_multi_variable_assignment"
1105- )
1106- targets_arg = List (
1107- [
1108- Dict (keys = list (target ), values = list (target .values ()))
1109- for target in targets
1110- ],
1111- ctx = Load (),
1112- )
1113- node .value = Call (
1114- func_name ,
1115- [node .value , targets_arg , self ._memo .get_memo_name ()],
1116- [],
1117- )
1096+ func_name = self ._get_import (
1097+ "typeguard._functions" , "check_variable_assignment"
1098+ )
1099+ targets_arg = List (
1100+ [
1101+ List (
1102+ [Tuple ([name , ann ], ctx = Load ()) for name , ann in target ],
1103+ ctx = Load (),
1104+ )
1105+ for target in targets
1106+ ],
1107+ ctx = Load (),
1108+ )
1109+ node .value = Call (
1110+ func_name ,
1111+ [node .value , targets_arg , self ._memo .get_memo_name ()],
1112+ [],
1113+ )
11181114
11191115 return node
11201116
@@ -1175,12 +1171,20 @@ def visit_AugAssign(self, node: AugAssign) -> Any:
11751171 operator_call = Call (
11761172 operator_func , [Name (node .target .id , ctx = Load ()), node .value ], []
11771173 )
1174+ targets_arg = List (
1175+ [
1176+ List (
1177+ [Tuple ([Constant (node .target .id ), annotation ], ctx = Load ())],
1178+ ctx = Load (),
1179+ )
1180+ ],
1181+ ctx = Load (),
1182+ )
11781183 check_call = Call (
11791184 self ._get_import ("typeguard._functions" , "check_variable_assignment" ),
11801185 [
11811186 operator_call ,
1182- Constant (node .target .id ),
1183- annotation ,
1187+ targets_arg ,
11841188 self ._memo .get_memo_name (),
11851189 ],
11861190 [],
0 commit comments