Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/autograph/pyct/static_analysis/activity.py: 19%

411 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15"""Activity analysis. 

16 

17Requires qualified name annotations (see qual_names.py). 

18""" 

19 

20import copy 

21import weakref 

22 

23import gast 

24 

25from tensorflow.python.autograph.pyct import anno 

26from tensorflow.python.autograph.pyct import qual_names 

27from tensorflow.python.autograph.pyct import transformer 

28from tensorflow.python.autograph.pyct.static_analysis.annos import NodeAnno 

29 

30 

31class Scope(object): 

32 """Encloses local symbol definition and usage information. 

33 

34 This can track for instance whether a symbol is modified in the current scope. 

35 Note that scopes do not necessarily align with Python's scopes. For example, 

36 the body of an if statement may be considered a separate scope. 

37 

38 Caution - the AST references held by this object are weak. 

39 

40 Scope objects are mutable during construction only, and must be frozen using 

41 `Scope.finalize()` before use. Furthermore, a scope is consistent only after 

42 all its children have been frozen. While analysing code blocks, scopes are 

43 being gradually built, from the innermost scope outward. Freezing indicates 

44 that the analysis of a code block is complete. Once frozen, mutation is no 

45 longer allowed. `is_final` tracks whether the scope is frozen or not. Certain 

46 properties, like `referenced`, are only accurate when called on frozen scopes. 

47 

48 Attributes: 

49 parent: Optional[Scope], the parent scope, if any. 

50 isolated: bool, whether the scope is a true Python scope (e.g. the scope of 

51 a function), or just a surrogate tracking an ordinary code block. Using 

52 the terminology of the Python 3 reference documentation, True roughly 

53 represents an actual scope, whereas False represents an ordinary code 

54 block. 

55 function_name: Optional[str], name of the function owning this scope. 

56 isolated_names: Set[qual_names.QN], identifiers that are isolated to this 

57 scope (even if the scope is not isolated). 

58 annotations: Set[qual_names.QN], identifiers used as type annotations 

59 in this scope. 

60 read: Set[qual_names.QN], identifiers read in this scope. 

61 modified: Set[qual_names.QN], identifiers modified in this scope. 

62 deleted: Set[qual_names.QN], identifiers deleted in this scope. 

63 bound: Set[qual_names.QN], names that are bound to this scope. See 

64 https://docs.python.org/3/reference/executionmodel.html#binding-of-names 

65 for a precise definition. 

66 globals: Set[qual_names.QN], names that are explicitly marked as global in 

67 this scope. Note that this doesn't include free read-only vars bound to 

68 global symbols. 

69 nonlocals: Set[qual_names.QN], names that are explicitly marked as nonlocal 

70 in this scope. Note that this doesn't include free read-only vars bound to 

71 global symbols. 

72 free_vars: Set[qual_names.QN], the free variables in this scope. See 

73 https://docs.python.org/3/reference/executionmodel.html for a precise 

74 definition. 

75 params: WeakValueDictionary[qual_names.QN, ast.Node], function arguments 

76 visible in this scope, mapped to the function node that defines them. 

77 enclosing_scope: Scope, the innermost isolated scope that is a transitive 

78 parent of this scope. May be the scope itself. 

79 referenced: Set[qual_names.QN], the totality of the symbols used by this 

80 scope and its parents. 

81 is_final: bool, whether the scope is frozen or not. 

82 

83 Note - simple statements may never delete and modify a symbol at the same 

84 time. However, compound ones like if statements can. In that latter case, it's 

85 undefined whether the symbol is actually modified or deleted upon statement 

86 exit. Certain analyses like reaching definitions need to be careful about 

87 this. 

88 """ 

89 

90 # Note: this mutable-immutable pattern is used because using a builder would 

91 # have taken a lot more boilerplate. 

92 

93 def __init__(self, parent, isolated=True, function_name=None): 

94 """Create a new scope. 

95 

96 Args: 

97 parent: A Scope or None. 

98 isolated: Whether the scope is isolated, that is, whether variables 

99 modified in this scope should be considered modified in the parent 

100 scope. 

101 function_name: Name of the function owning this scope. 

102 """ 

103 self.parent = parent 

104 self.isolated = isolated 

105 self.function_name = function_name 

106 

107 self.isolated_names = set() 

108 

109 self.read = set() 

110 self.modified = set() 

111 self.deleted = set() 

112 

113 self.bound = set() 

114 self.globals = set() 

115 self.nonlocals = set() 

116 self.annotations = set() 

117 

118 self.params = weakref.WeakValueDictionary() 

119 

120 # Certain fields can only be accessed after the scope and all its parent 

121 # scopes have been fully built. This field guards that. 

122 self.is_final = False 

123 

124 @property 

125 def enclosing_scope(self): 

126 assert self.is_final 

127 if self.parent is not None and not self.isolated: 

128 return self.parent 

129 return self 

130 

131 @property 

132 def referenced(self): 

133 if self.parent is not None: 

134 return self.read | self.parent.referenced 

135 return self.read 

136 

137 @property 

138 def free_vars(self): 

139 enclosing_scope = self.enclosing_scope 

140 return enclosing_scope.read - enclosing_scope.bound 

141 

142 def copy_from(self, other): 

143 """Recursively copies the contents of this scope from another scope.""" 

144 assert not self.is_final 

145 if self.parent is not None: 

146 assert other.parent is not None 

147 self.parent.copy_from(other.parent) 

148 self.isolated_names = copy.copy(other.isolated_names) 

149 self.modified = copy.copy(other.modified) 

150 self.read = copy.copy(other.read) 

151 self.deleted = copy.copy(other.deleted) 

152 self.bound = copy.copy(other.bound) 

153 self.annotations = copy.copy(other.annotations) 

154 self.params = copy.copy(other.params) 

155 

156 @classmethod 

157 def copy_of(cls, other): 

158 if other.parent is not None: 

159 assert other.parent is not None 

160 parent = cls.copy_of(other.parent) 

161 else: 

162 parent = None 

163 new_copy = cls(parent) 

164 new_copy.copy_from(other) 

165 return new_copy 

166 

167 def merge_from(self, other): 

168 """Adds all activity from another scope to this scope.""" 

169 assert not self.is_final 

170 if self.parent is not None: 

171 assert other.parent is not None 

172 self.parent.merge_from(other.parent) 

173 self.isolated_names.update(other.isolated_names) 

174 self.read.update(other.read) 

175 self.modified.update(other.modified) 

176 self.bound.update(other.bound) 

177 self.deleted.update(other.deleted) 

178 self.annotations.update(other.annotations) 

179 self.params.update(other.params) 

180 

181 def finalize(self): 

182 """Freezes this scope.""" 

183 assert not self.is_final 

184 # TODO(mdan): freeze read, modified, bound. 

185 if self.parent is not None: 

186 assert not self.parent.is_final 

187 if not self.isolated: 

188 self.parent.read.update(self.read - self.isolated_names) 

189 self.parent.modified.update(self.modified - self.isolated_names) 

190 self.parent.bound.update(self.bound - self.isolated_names) 

191 self.parent.globals.update(self.globals) 

192 self.parent.nonlocals.update(self.nonlocals) 

193 self.parent.annotations.update(self.annotations) 

194 else: 

195 # TODO(mdan): This is not accurate. 

196 self.parent.read.update(self.read - self.bound) 

197 self.parent.annotations.update(self.annotations - self.bound) 

198 self.is_final = True 

199 

200 def __repr__(self): 

201 return 'Scope{r=%s, w=%s}' % (tuple(self.read), tuple(self.modified)) 

202 

203 def mark_param(self, name, owner): 

204 # Assumption: all AST nodes have the same life span. This lets us use 

205 # a weak reference to mark the connection between a symbol node and the 

206 # function node whose argument that symbol is. 

207 self.params[name] = owner 

208 

209 

210class _Comprehension(object): 

211 

212 no_root = True 

213 

214 def __init__(self): 

215 # TODO(mdan): Consider using an enum. 

216 self.is_list_comp = False 

217 self.targets = set() 

218 

219 

220class _FunctionOrClass(object): 

221 

222 def __init__(self): 

223 self.node = None 

224 

225 

226class ActivityAnalyzer(transformer.Base): 

227 """Annotates nodes with local scope information. 

228 

229 See Scope. 

230 

231 The use of this class requires that qual_names.resolve() has been called on 

232 the node. This class will ignore nodes have not been 

233 annotated with their qualified names. 

234 """ 

235 

236 def __init__(self, context, parent_scope=None): 

237 super(ActivityAnalyzer, self).__init__(context) 

238 self.allow_skips = False 

239 self.scope = Scope(parent_scope, isolated=True) 

240 

241 # Note: all these flags crucially rely on the respective nodes are 

242 # leaves in the AST, that is, they cannot contain other statements. 

243 self._in_aug_assign = False 

244 self._in_annotation = False 

245 self._track_annotations_only = False 

246 

247 @property 

248 def _in_constructor(self): 

249 context = self.state[_FunctionOrClass] 

250 if context.level > 2: 

251 innermost = context.stack[-1].node 

252 parent = context.stack[-2].node 

253 return (isinstance(parent, gast.ClassDef) and 

254 (isinstance(innermost, gast.FunctionDef) and 

255 innermost.name == '__init__')) 

256 return False 

257 

258 def _node_sets_self_attribute(self, node): 

259 if anno.hasanno(node, anno.Basic.QN): 

260 qn = anno.getanno(node, anno.Basic.QN) 

261 # TODO(mdan): The 'self' argument is not guaranteed to be called 'self'. 

262 if qn.has_attr and qn.parent.qn == ('self',): 

263 return True 

264 return False 

265 

266 def _track_symbol(self, node, composite_writes_alter_parent=False): 

267 if self._track_annotations_only and not self._in_annotation: 

268 return 

269 

270 # A QN may be missing when we have an attribute (or subscript) on a function 

271 # call. Example: a().b 

272 if not anno.hasanno(node, anno.Basic.QN): 

273 return 

274 qn = anno.getanno(node, anno.Basic.QN) 

275 

276 # When inside a comprehension, ignore reads to any of the comprehensions's 

277 # targets. This includes attributes or slices of those arguments. 

278 for l in self.state[_Comprehension]: 

279 if qn in l.targets: 

280 return 

281 if qn.owner_set & set(l.targets): 

282 return 

283 

284 if isinstance(node.ctx, gast.Store): 

285 # In comprehensions, modified symbols are the comprehension targets. 

286 if self.state[_Comprehension].level > 0: 

287 self.state[_Comprehension].targets.add(qn) 

288 return 

289 

290 self.scope.modified.add(qn) 

291 self.scope.bound.add(qn) 

292 if qn.is_composite and composite_writes_alter_parent: 

293 self.scope.modified.add(qn.parent) 

294 if self._in_aug_assign: 

295 self.scope.read.add(qn) 

296 

297 elif isinstance(node.ctx, gast.Load): 

298 self.scope.read.add(qn) 

299 if self._in_annotation: 

300 self.scope.annotations.add(qn) 

301 

302 elif isinstance(node.ctx, gast.Param): 

303 self.scope.bound.add(qn) 

304 self.scope.mark_param(qn, self.state[_FunctionOrClass].node) 

305 

306 elif isinstance(node.ctx, gast.Del): 

307 # The read matches the Python semantics - attempting to delete an 

308 # undefined symbol is illegal. 

309 self.scope.read.add(qn) 

310 # Targets of del are considered bound: 

311 # https://docs.python.org/3/reference/executionmodel.html#binding-of-names 

312 self.scope.bound.add(qn) 

313 self.scope.deleted.add(qn) 

314 

315 else: 

316 raise ValueError('Unknown context {} for node "{}".'.format( 

317 type(node.ctx), qn)) 

318 

319 def _enter_scope(self, isolated, f_name=None): 

320 self.scope = Scope(self.scope, isolated=isolated, function_name=f_name) 

321 

322 def _exit_scope(self): 

323 exited_scope = self.scope 

324 exited_scope.finalize() 

325 self.scope = exited_scope.parent 

326 return exited_scope 

327 

328 def _exit_and_record_scope(self, node, tag=anno.Static.SCOPE): 

329 node_scope = self._exit_scope() 

330 anno.setanno(node, tag, node_scope) 

331 return node_scope 

332 

333 def _process_statement(self, node): 

334 self._enter_scope(False) 

335 node = self.generic_visit(node) 

336 self._exit_and_record_scope(node) 

337 return node 

338 

339 def _process_annotation(self, node): 

340 self._in_annotation = True 

341 node = self.visit(node) 

342 self._in_annotation = False 

343 return node 

344 

345 def visit_Import(self, node): 

346 return self._process_statement(node) 

347 

348 def visit_ImportFrom(self, node): 

349 return self._process_statement(node) 

350 

351 def visit_Global(self, node): 

352 self._enter_scope(False) 

353 for name in node.names: 

354 qn = qual_names.QN(name) 

355 self.scope.read.add(qn) 

356 self.scope.globals.add(qn) 

357 self._exit_and_record_scope(node) 

358 return node 

359 

360 def visit_Nonlocal(self, node): 

361 self._enter_scope(False) 

362 for name in node.names: 

363 qn = qual_names.QN(name) 

364 self.scope.read.add(qn) 

365 self.scope.bound.add(qn) 

366 self.scope.nonlocals.add(qn) 

367 self._exit_and_record_scope(node) 

368 return node 

369 

370 def visit_Expr(self, node): 

371 return self._process_statement(node) 

372 

373 def visit_Raise(self, node): 

374 return self._process_statement(node) 

375 

376 def visit_Return(self, node): 

377 return self._process_statement(node) 

378 

379 def visit_Assign(self, node): 

380 return self._process_statement(node) 

381 

382 def visit_AnnAssign(self, node): 

383 self._enter_scope(False) 

384 node.target = self.visit(node.target) 

385 if node.value is not None: 

386 # Can be None for pure declarations, e.g. `n: int`. This is a new thing 

387 # enabled by type annotations, but does not influence static analysis 

388 # (declarations are not definitions). 

389 node.value = self.visit(node.value) 

390 if node.annotation: 

391 node.annotation = self._process_annotation(node.annotation) 

392 self._exit_and_record_scope(node) 

393 return node 

394 

395 def visit_AugAssign(self, node): 

396 # Special rules for AugAssign. Here, the AST only shows the target as 

397 # written, when it is in fact also read. 

398 self._enter_scope(False) 

399 

400 self._in_aug_assign = True 

401 node.target = self.visit(node.target) 

402 self._in_aug_assign = False 

403 

404 node.op = self.visit(node.op) 

405 node.value = self.visit(node.value) 

406 self._exit_and_record_scope(node) 

407 return node 

408 

409 def visit_Delete(self, node): 

410 return self._process_statement(node) 

411 

412 def visit_Name(self, node): 

413 if node.annotation: 

414 node.annotation = self._process_annotation(node.annotation) 

415 self._track_symbol(node) 

416 return node 

417 

418 def visit_alias(self, node): 

419 node = self.generic_visit(node) 

420 

421 if node.asname is None: 

422 # Only the root name is a real symbol operation. 

423 qn = qual_names.QN(node.name.split('.')[0]) 

424 else: 

425 qn = qual_names.QN(node.asname) 

426 

427 self.scope.modified.add(qn) 

428 self.scope.bound.add(qn) 

429 return node 

430 

431 def visit_Attribute(self, node): 

432 node = self.generic_visit(node) 

433 if self._in_constructor and self._node_sets_self_attribute(node): 

434 self._track_symbol(node, composite_writes_alter_parent=True) 

435 else: 

436 self._track_symbol(node) 

437 return node 

438 

439 def visit_Subscript(self, node): 

440 node = self.generic_visit(node) 

441 # Subscript writes (e.g. a[b] = "value") are considered to modify 

442 # both the element itself (a[b]) and its parent (a). 

443 self._track_symbol(node) 

444 return node 

445 

446 def visit_Print(self, node): 

447 self._enter_scope(False) 

448 node.values = self.visit_block(node.values) 

449 node_scope = self._exit_and_record_scope(node) 

450 anno.setanno(node, NodeAnno.ARGS_SCOPE, node_scope) 

451 return node 

452 

453 def visit_Assert(self, node): 

454 return self._process_statement(node) 

455 

456 def visit_Call(self, node): 

457 self._enter_scope(False) 

458 node.args = self.visit_block(node.args) 

459 node.keywords = self.visit_block(node.keywords) 

460 # TODO(mdan): Account starargs, kwargs 

461 self._exit_and_record_scope(node, tag=NodeAnno.ARGS_SCOPE) 

462 

463 node.func = self.visit(node.func) 

464 return node 

465 

466 def _process_block_node(self, node, block, scope_name): 

467 self._enter_scope(False) 

468 block = self.visit_block(block) 

469 self._exit_and_record_scope(node, tag=scope_name) 

470 return node 

471 

472 def _process_parallel_blocks(self, parent, children): 

473 # Because the scopes are not isolated, processing any child block 

474 # modifies the parent state causing the other child blocks to be 

475 # processed incorrectly. So we need to checkpoint the parent scope so that 

476 # each child sees the same context. 

477 before_parent = Scope.copy_of(self.scope) 

478 after_children = [] 

479 for child, scope_name in children: 

480 self.scope.copy_from(before_parent) 

481 parent = self._process_block_node(parent, child, scope_name) 

482 after_child = Scope.copy_of(self.scope) 

483 after_children.append(after_child) 

484 for after_child in after_children: 

485 self.scope.merge_from(after_child) 

486 return parent 

487 

488 def _process_comprehension(self, 

489 node, 

490 is_list_comp=False, 

491 is_dict_comp=False): 

492 with self.state[_Comprehension] as comprehension_: 

493 comprehension_.is_list_comp = is_list_comp 

494 # Note: it's important to visit the generators first to properly account 

495 # for the variables local to these generators. Example: `x` is local to 

496 # the expression `z for x in y for z in x`. 

497 node.generators = self.visit_block(node.generators) 

498 if is_dict_comp: 

499 node.key = self.visit(node.key) 

500 node.value = self.visit(node.value) 

501 else: 

502 node.elt = self.visit(node.elt) 

503 return node 

504 

505 def visit_comprehension(self, node): 

506 # It is important to visit children in this order so that the reads to 

507 # the target name are appropriately ignored. 

508 node.iter = self.visit(node.iter) 

509 node.target = self.visit(node.target) 

510 return self.generic_visit(node) 

511 

512 def visit_DictComp(self, node): 

513 return self._process_comprehension(node, is_dict_comp=True) 

514 

515 def visit_ListComp(self, node): 

516 return self._process_comprehension(node, is_list_comp=True) 

517 

518 def visit_SetComp(self, node): 

519 return self._process_comprehension(node) 

520 

521 def visit_GeneratorExp(self, node): 

522 return self._process_comprehension(node) 

523 

524 def visit_ClassDef(self, node): 

525 with self.state[_FunctionOrClass] as fn: 

526 fn.node = node 

527 # The ClassDef node itself has a Scope object that tracks the creation 

528 # of its name, along with the usage of any decorator accompanying it. 

529 self._enter_scope(False) 

530 node.decorator_list = self.visit_block(node.decorator_list) 

531 self.scope.modified.add(qual_names.QN(node.name)) 

532 self.scope.bound.add(qual_names.QN(node.name)) 

533 node.bases = self.visit_block(node.bases) 

534 node.keywords = self.visit_block(node.keywords) 

535 self._exit_and_record_scope(node) 

536 

537 # A separate Scope tracks the actual class definition. 

538 self._enter_scope(True) 

539 node = self.generic_visit(node) 

540 self._exit_scope() 

541 return node 

542 

543 def _visit_node_list(self, nodes): 

544 return [(None if n is None else self.visit(n)) for n in nodes] 

545 

546 def _visit_arg_annotations(self, node): 

547 node.args.kw_defaults = self._visit_node_list(node.args.kw_defaults) 

548 node.args.defaults = self._visit_node_list(node.args.defaults) 

549 self._track_annotations_only = True 

550 node = self._visit_arg_declarations(node) 

551 self._track_annotations_only = False 

552 return node 

553 

554 def _visit_arg_declarations(self, node): 

555 node.args.posonlyargs = self._visit_node_list(node.args.posonlyargs) 

556 node.args.args = self._visit_node_list(node.args.args) 

557 if node.args.vararg is not None: 

558 node.args.vararg = self.visit(node.args.vararg) 

559 node.args.kwonlyargs = self._visit_node_list(node.args.kwonlyargs) 

560 if node.args.kwarg is not None: 

561 node.args.kwarg = self.visit(node.args.kwarg) 

562 return node 

563 

564 def visit_FunctionDef(self, node): 

565 with self.state[_FunctionOrClass] as fn: 

566 fn.node = node 

567 # The FunctionDef node itself has a Scope object that tracks the creation 

568 # of its name, along with the usage of any decorator accompanying it. 

569 self._enter_scope(False) 

570 node.decorator_list = self.visit_block(node.decorator_list) 

571 if node.returns: 

572 node.returns = self._process_annotation(node.returns) 

573 # Argument annotartions (includeing defaults) affect the defining context. 

574 node = self._visit_arg_annotations(node) 

575 

576 function_name = qual_names.QN(node.name) 

577 self.scope.modified.add(function_name) 

578 self.scope.bound.add(function_name) 

579 self._exit_and_record_scope(node) 

580 

581 # A separate Scope tracks the actual function definition. 

582 self._enter_scope(True, node.name) 

583 

584 # Keep a separate scope for the arguments node, which is used in the CFG. 

585 self._enter_scope(False, node.name) 

586 

587 # Arg declarations only affect the function itself, and have no effect 

588 # in the defining context whatsoever. 

589 node = self._visit_arg_declarations(node) 

590 

591 self._exit_and_record_scope(node.args) 

592 

593 # Track the body separately. This is for compatibility reasons, it may not 

594 # be strictly needed. 

595 self._enter_scope(False, node.name) 

596 node.body = self.visit_block(node.body) 

597 self._exit_and_record_scope(node, NodeAnno.BODY_SCOPE) 

598 

599 self._exit_and_record_scope(node, NodeAnno.ARGS_AND_BODY_SCOPE) 

600 return node 

601 

602 def visit_Lambda(self, node): 

603 # Lambda nodes are treated in roughly the same way as FunctionDef nodes. 

604 with self.state[_FunctionOrClass] as fn: 

605 fn.node = node 

606 # The Lambda node itself has a Scope object that tracks the creation 

607 # of its name, along with the usage of any decorator accompanying it. 

608 self._enter_scope(False) 

609 node = self._visit_arg_annotations(node) 

610 self._exit_and_record_scope(node) 

611 

612 # A separate Scope tracks the actual function definition. 

613 self._enter_scope(True) 

614 

615 # Keep a separate scope for the arguments node, which is used in the CFG. 

616 self._enter_scope(False) 

617 node = self._visit_arg_declarations(node) 

618 self._exit_and_record_scope(node.args) 

619 

620 # Track the body separately. This is for compatibility reasons, it may not 

621 # be strictly needed. 

622 # TODO(mdan): Do remove it, it's confusing. 

623 self._enter_scope(False) 

624 node.body = self.visit(node.body) 

625 

626 # The lambda body can contain nodes of types normally not found as 

627 # statements, and may not have the SCOPE annotation needed by the CFG. 

628 # So we attach one if necessary. 

629 if not anno.hasanno(node.body, anno.Static.SCOPE): 

630 anno.setanno(node.body, anno.Static.SCOPE, self.scope) 

631 

632 self._exit_and_record_scope(node, NodeAnno.BODY_SCOPE) 

633 

634 lambda_scope = self.scope 

635 self._exit_and_record_scope(node, NodeAnno.ARGS_AND_BODY_SCOPE) 

636 

637 # TODO(bhack:) https://github.com/tensorflow/tensorflow/issues/56089 

638 # remove after deprecation 

639 # Exception: lambdas are assumed to be used in the place where 

640 # they are defined. Therefore, their activity is passed on to the 

641 # calling statement. 

642 self.scope.read.update(lambda_scope.read - lambda_scope.bound) 

643 

644 return node 

645 

646 def visit_With(self, node): 

647 self._enter_scope(False) 

648 node = self.generic_visit(node) 

649 self._exit_and_record_scope(node, NodeAnno.BODY_SCOPE) 

650 return node 

651 

652 def visit_withitem(self, node): 

653 return self._process_statement(node) 

654 

655 def visit_If(self, node): 

656 self._enter_scope(False) 

657 node.test = self.visit(node.test) 

658 node_scope = self._exit_and_record_scope(node.test) 

659 anno.setanno(node, NodeAnno.COND_SCOPE, node_scope) 

660 

661 node = self._process_parallel_blocks(node, 

662 ((node.body, NodeAnno.BODY_SCOPE), 

663 (node.orelse, NodeAnno.ORELSE_SCOPE))) 

664 return node 

665 

666 def visit_For(self, node): 

667 self._enter_scope(False) 

668 node.target = self.visit(node.target) 

669 node.iter = self.visit(node.iter) 

670 self._exit_and_record_scope(node.iter) 

671 

672 self._enter_scope(False) 

673 self.visit(node.target) 

674 if anno.hasanno(node, anno.Basic.EXTRA_LOOP_TEST): 

675 self._process_statement(anno.getanno(node, anno.Basic.EXTRA_LOOP_TEST)) 

676 self._exit_and_record_scope(node, tag=NodeAnno.ITERATE_SCOPE) 

677 

678 node = self._process_parallel_blocks(node, 

679 ((node.body, NodeAnno.BODY_SCOPE), 

680 (node.orelse, NodeAnno.ORELSE_SCOPE))) 

681 return node 

682 

683 def visit_While(self, node): 

684 self._enter_scope(False) 

685 node.test = self.visit(node.test) 

686 node_scope = self._exit_and_record_scope(node.test) 

687 anno.setanno(node, NodeAnno.COND_SCOPE, node_scope) 

688 

689 node = self._process_parallel_blocks(node, 

690 ((node.body, NodeAnno.BODY_SCOPE), 

691 (node.orelse, NodeAnno.ORELSE_SCOPE))) 

692 return node 

693 

694 def visit_ExceptHandler(self, node): 

695 self._enter_scope(False) 

696 # try/except oddity: as expected, it leaks any names you defined inside the 

697 # except block, but not the name of the exception variable. 

698 if node.name is not None: 

699 self.scope.isolated_names.add(anno.getanno(node.name, anno.Basic.QN)) 

700 node = self.generic_visit(node) 

701 self._exit_scope() 

702 return node 

703 

704 

705def resolve(node, context, parent_scope=None): 

706 return ActivityAnalyzer(context, parent_scope).visit(node)