@@ -116,7 +116,7 @@ def get_dependencies_of_target(module_id: str,
116
116
"""Get dependencies of a target -- don't recursive into nested targets."""
117
117
# TODO: Add tests for this function.
118
118
visitor = DependencyVisitor (type_map , python_version )
119
- visitor .enter_file_scope (module_id )
119
+ visitor .scope . enter_file (module_id )
120
120
if isinstance (target , MypyFile ):
121
121
# Only get dependencies of the top-level of the module. Don't recurse into
122
122
# functions.
@@ -127,25 +127,20 @@ def get_dependencies_of_target(module_id: str,
127
127
elif isinstance (target , FuncBase ) and target .info :
128
128
# It's a method.
129
129
# TODO: Methods in nested classes.
130
- visitor .enter_class_scope (target .info )
130
+ visitor .scope . enter_class (target .info )
131
131
target .accept (visitor )
132
- visitor .leave_scope ()
132
+ visitor .scope . leave ()
133
133
else :
134
134
target .accept (visitor )
135
- visitor .leave_scope ()
135
+ visitor .scope . leave ()
136
136
return visitor .map
137
137
138
138
139
139
class DependencyVisitor (TraverserVisitor ):
140
140
def __init__ (self ,
141
141
type_map : Dict [Expression , Type ],
142
142
python_version : Tuple [int , int ]) -> None :
143
- # Stack of names of targets being processed. For stack targets we use the
144
- # surrounding module.
145
- self .target_stack = [] # type: List[str]
146
- # Stack of names of targets being processed, including class targets.
147
- self .full_target_stack = [] # type: List[str]
148
- self .scope_stack = [] # type: List[Union[None, TypeInfo, FuncDef]]
143
+ self .scope = Scope ()
149
144
self .type_map = type_map
150
145
self .python2 = python_version [0 ] == 2
151
146
self .map = {} # type: Dict[str, Set[str]]
@@ -165,20 +160,14 @@ def __init__(self,
165
160
# type variable with value restriction
166
161
167
162
def visit_mypy_file (self , o : MypyFile ) -> None :
168
- self .enter_file_scope (o .fullname ())
163
+ self .scope . enter_file (o .fullname ())
169
164
self .is_package_init_file = o .is_package_init_file ()
170
165
super ().visit_mypy_file (o )
171
- self .leave_scope ()
166
+ self .scope . leave ()
172
167
173
168
def visit_func_def (self , o : FuncDef ) -> None :
174
- if not isinstance (self .current_scope (), FuncDef ):
175
- # Not a nested function, so create a new target.
176
- new_scope = True
177
- target = self .enter_function_scope (o )
178
- else :
179
- # Treat nested functions as components of the parent function target.
180
- new_scope = False
181
- target = self .current_target ()
169
+ self .scope .enter_function (o )
170
+ target = self .scope .current_target ()
182
171
if o .type :
183
172
if self .is_class and isinstance (o .type , FunctionLike ):
184
173
signature = bind_self (o .type ) # type: Type
@@ -191,15 +180,15 @@ def visit_func_def(self, o: FuncDef) -> None:
191
180
for base in non_trivial_bases (o .info ):
192
181
self .add_dependency (make_trigger (base .fullname () + '.' + o .name ()))
193
182
super ().visit_func_def (o )
194
- if new_scope :
195
- self .leave_scope ()
183
+ self .scope .leave ()
196
184
197
185
def visit_decorator (self , o : Decorator ) -> None :
198
186
self .add_dependency (make_trigger (o .func .fullname ()))
199
187
super ().visit_decorator (o )
200
188
201
189
def visit_class_def (self , o : ClassDef ) -> None :
202
- target = self .enter_class_scope (o .info )
190
+ self .scope .enter_class (o .info )
191
+ target = self .scope .current_full_target ()
203
192
self .add_dependency (make_trigger (target ), target )
204
193
old_is_class = self .is_class
205
194
self .is_class = True
@@ -226,15 +215,15 @@ def visit_class_def(self, o: ClassDef) -> None:
226
215
target = make_trigger (info .fullname () + '.' + name ))
227
216
self .add_dependency (make_trigger (base_info .fullname () + '.__init__' ),
228
217
target = make_trigger (info .fullname () + '.__init__' ))
229
- self .leave_scope ()
218
+ self .scope . leave ()
230
219
231
220
def visit_import (self , o : Import ) -> None :
232
221
for id , as_id in o .ids :
233
222
# TODO: as_id
234
- self .add_dependency (make_trigger (id ), self .current_target ())
223
+ self .add_dependency (make_trigger (id ), self .scope . current_target ())
235
224
236
225
def visit_import_from (self , o : ImportFrom ) -> None :
237
- module_id , _ = correct_relative_import (self .current_module_id (),
226
+ module_id , _ = correct_relative_import (self .scope . current_module_id (),
238
227
o .relative ,
239
228
o .id ,
240
229
self .is_package_init_file )
@@ -260,7 +249,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
260
249
elif isinstance (rvalue , CallExpr ) and isinstance (rvalue .analyzed , NamedTupleExpr ):
261
250
# Depend on types of named tuple items.
262
251
info = rvalue .analyzed .info
263
- prefix = '%s.%s' % (self .current_full_target (), info .name ())
252
+ prefix = '%s.%s' % (self .scope . current_full_target (), info .name ())
264
253
for name , symnode in info .names .items ():
265
254
if not name .startswith ('_' ) and isinstance (symnode .node , Var ):
266
255
typ = symnode .node .type
@@ -293,7 +282,8 @@ def process_lvalue(self, lvalue: Expression) -> None:
293
282
# global variable.
294
283
lvalue_type = self .get_non_partial_lvalue_type (lvalue )
295
284
type_triggers = get_type_triggers (lvalue_type )
296
- attr_trigger = make_trigger ('%s.%s' % (self .full_target_stack [- 1 ], lvalue .name ))
285
+ attr_trigger = make_trigger ('%s.%s' % (self .scope .current_full_target (),
286
+ lvalue .name ))
297
287
for type_trigger in type_triggers :
298
288
self .add_dependency (type_trigger , attr_trigger )
299
289
elif isinstance (lvalue , MemberExpr ):
@@ -515,7 +505,7 @@ def add_dependency(self, trigger: str, target: Optional[str] = None) -> None:
515
505
# anyway.
516
506
return
517
507
if target is None :
518
- target = self .current_target ()
508
+ target = self .scope . current_target ()
519
509
self .map .setdefault (trigger , set ()).add (target )
520
510
521
511
def add_type_dependencies (self , typ : Type , target : Optional [str ] = None ) -> None :
@@ -569,50 +559,77 @@ def add_iter_dependency(self, node: Expression) -> None:
569
559
if typ :
570
560
self .add_attribute_dependency (typ , '__iter__' )
571
561
572
- def current_module_id (self ) -> str :
573
- return self .target_stack [0 ]
574
-
575
- def enter_file_scope (self , prefix : str ) -> None :
576
- """Enter a module target scope."""
577
- assert not self .target_stack
578
- self .target_stack .append (prefix )
579
- self .full_target_stack .append (prefix )
580
- self .scope_stack .append (None )
581
-
582
- def enter_function_scope (self , fdef : FuncDef ) -> str :
583
- """Enter a function target scope."""
584
- target = '%s.%s' % (self .full_target_stack [- 1 ], fdef .name ())
585
- self .target_stack .append (target )
586
- self .full_target_stack .append (target )
587
- self .scope_stack .append (fdef )
588
- return target
589
562
590
- def enter_class_scope (self , info : TypeInfo ) -> str :
591
- """Enter a class target scope."""
592
- # Duplicate the previous top non-class target (it can't be a class but since the
593
- # depths of all stacks must agree we need something).
594
- self .target_stack .append (self .target_stack [- 1 ])
595
- full_target = '%s.%s' % (self .full_target_stack [- 1 ], info .name ())
596
- self .full_target_stack .append (full_target )
597
- self .scope_stack .append (info )
598
- return full_target
599
-
600
- def leave_scope (self ) -> None :
601
- """Leave a target scope."""
602
- self .target_stack .pop ()
603
- self .full_target_stack .pop ()
604
- self .scope_stack .pop ()
563
+ class Scope :
564
+ """Track which target we are processing at any given time."""
565
+
566
+ def __init__ (self ) -> None :
567
+ self .module = None # type: Optional[str]
568
+ self .classes = [] # type: List[TypeInfo]
569
+ self .function = None # type: Optional[FuncDef]
570
+ # Number of nested scopes ignored (that don't get their own separate targets)
571
+ self .ignored = 0
572
+
573
+ def current_module_id (self ) -> str :
574
+ assert self .module
575
+ return self .module
605
576
606
577
def current_target (self ) -> str :
607
578
"""Return the current target (non-class; for a class return enclosing module)."""
608
- return self .target_stack [- 1 ]
579
+ assert self .module
580
+ target = self .module
581
+ if self .function :
582
+ if self .classes :
583
+ target += '.' + '.' .join (c .name () for c in self .classes )
584
+ target += '.' + self .function .name ()
585
+ return target
609
586
610
587
def current_full_target (self ) -> str :
611
588
"""Return the current target (may be a class)."""
612
- return self .full_target_stack [- 1 ]
589
+ assert self .module
590
+ target = self .module
591
+ if self .classes :
592
+ target += '.' + '.' .join (c .name () for c in self .classes )
593
+ if self .function :
594
+ target += '.' + self .function .name ()
595
+ return target
596
+
597
+ def enter_file (self , prefix : str ) -> None :
598
+ self .module = prefix
599
+ self .classes = []
600
+ self .function = None
601
+ self .ignored = 0
602
+
603
+ def enter_function (self , fdef : FuncDef ) -> None :
604
+ if not self .function :
605
+ self .function = fdef
606
+ else :
607
+ # Nested functions are part of the topmost function target.
608
+ self .ignored += 1
613
609
614
- def current_scope (self ) -> Optional [Node ]:
615
- return self .scope_stack [- 1 ]
610
+ def enter_class (self , info : TypeInfo ) -> None :
611
+ """Enter a class target scope."""
612
+ if not self .function :
613
+ self .classes .append (info )
614
+ else :
615
+ # Classes within functions are part of the enclosing function target.
616
+ self .ignored += 1
617
+
618
+ def leave (self ) -> None :
619
+ """Leave the innermost scope (can be any kind of scope)."""
620
+ if self .ignored :
621
+ # Leave a scope that's included in the enclosing target.
622
+ self .ignored -= 1
623
+ elif self .function :
624
+ # Function is always the innermost target.
625
+ self .function = None
626
+ elif self .classes :
627
+ # Leave the innermost class.
628
+ self .classes .pop ()
629
+ else :
630
+ # Leave module.
631
+ assert self .module
632
+ self .module = None
616
633
617
634
618
635
def get_type_triggers (typ : Type ) -> List [str ]:
0 commit comments