Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/matchers/_visitors.py: 71%

239 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:43 +0000

1# Copyright (c) Meta Platforms, Inc. and affiliates. 

2# 

3# This source code is licensed under the MIT license found in the 

4# LICENSE file in the root directory of this source tree. 

5 

6from inspect import ismethod, signature 

7from typing import ( 

8 Any, 

9 Callable, 

10 cast, 

11 Dict, 

12 get_type_hints, 

13 List, 

14 Optional, 

15 Sequence, 

16 Set, 

17 Tuple, 

18 Type, 

19 Union, 

20) 

21 

22import libcst as cst 

23from libcst import CSTTransformer, CSTVisitor 

24from libcst._types import CSTNodeT 

25from libcst.matchers._decorators import ( 

26 CONSTRUCTED_LEAVE_MATCHER_ATTR, 

27 CONSTRUCTED_VISIT_MATCHER_ATTR, 

28 VISIT_NEGATIVE_MATCHER_ATTR, 

29 VISIT_POSITIVE_MATCHER_ATTR, 

30) 

31from libcst.matchers._matcher_base import ( 

32 AllOf, 

33 AtLeastN, 

34 AtMostN, 

35 BaseMatcherNode, 

36 extract, 

37 extractall, 

38 findall, 

39 matches, 

40 MatchIfTrue, 

41 MatchMetadata, 

42 MatchMetadataIfTrue, 

43 OneOf, 

44 replace, 

45) 

46from libcst.matchers._return_types import TYPED_FUNCTION_RETURN_MAPPING 

47 

48try: 

49 # PEP 604 unions, in Python 3.10+ 

50 from types import UnionType 

51except ImportError: 

52 # We use this for isinstance; no annotation will be an instance of this 

53 class UnionType: 

54 pass 

55 

56 

57CONCRETE_METHODS: Set[str] = { 

58 *{f"visit_{cls.__name__}" for cls in TYPED_FUNCTION_RETURN_MAPPING}, 

59 *{f"leave_{cls.__name__}" for cls in TYPED_FUNCTION_RETURN_MAPPING}, 

60} 

61 

62 

63# pyre-ignore We don't care about Any here, its not exposed. 

64def _match_decorator_unpickler(kwargs: Any) -> "MatchDecoratorMismatch": 

65 return MatchDecoratorMismatch(**kwargs) 

66 

67 

68class MatchDecoratorMismatch(Exception): 

69 def __init__(self, func: str, message: str) -> None: 

70 super().__init__(f"Invalid function signature for {func}: {message}") 

71 self.func = func 

72 self.message = message 

73 

74 def __reduce__( 

75 self, 

76 ) -> Tuple[Callable[..., "MatchDecoratorMismatch"], Tuple[object, ...]]: 

77 return ( 

78 _match_decorator_unpickler, 

79 ({"func": self.func, "message": self.message},), 

80 ) 

81 

82 

83def _get_possible_match_classes(matcher: BaseMatcherNode) -> List[Type[cst.CSTNode]]: 

84 if isinstance(matcher, (OneOf, AllOf)): 

85 return [getattr(cst, m.__class__.__name__) for m in matcher.options] 

86 else: 

87 return [getattr(cst, matcher.__class__.__name__)] 

88 

89 

90def _annotation_is_union(annotation: object) -> bool: 

91 return ( 

92 isinstance(annotation, UnionType) 

93 or getattr(annotation, "__origin__", None) is Union 

94 ) 

95 

96 

97def _get_possible_annotated_classes(annotation: object) -> List[Type[object]]: 

98 if _annotation_is_union(annotation): 

99 return getattr(annotation, "__args__", []) 

100 else: 

101 return [cast(Type[object], annotation)] 

102 

103 

104def _get_valid_leave_annotations_for_classes( 

105 classes: Sequence[Type[cst.CSTNode]], 

106) -> Set[Type[object]]: 

107 retval: Set[Type[object]] = set() 

108 

109 for cls in classes: 

110 # Look up the leave annotation for each class, combine them so we get a list of 

111 # all possible valid return annotations. Its not really possible for us (or 

112 # pyre) to fully enforce return types given the presence of OneOf/AllOf matchers, so 

113 # we do the best we can by taking a union of all valid return annotations. 

114 retval.update( 

115 _get_possible_annotated_classes(TYPED_FUNCTION_RETURN_MAPPING[cls]) 

116 ) 

117 

118 return retval 

119 

120 

121def _verify_return_annotation( 

122 possible_match_classes: Sequence[Type[cst.CSTNode]], 

123 # pyre-ignore We only care that meth is callable. 

124 meth: Callable[..., Any], 

125 decorator_name: str, 

126 *, 

127 expected_none: bool, 

128) -> None: 

129 type_hints = get_type_hints(meth) 

130 if expected_none: 

131 # Simply look for any annotation at all and if it exists, verify that 

132 # it is "None". 

133 if type_hints.get("return", type(None)) is not type(None): # noqa: E721 

134 raise MatchDecoratorMismatch( 

135 # pyre-fixme[16]: Anonymous callable has no attribute `__qualname__`. 

136 meth.__qualname__, 

137 f"@{decorator_name} should only decorate functions that do " 

138 + "not return.", 

139 ) 

140 else: 

141 if "return" not in type_hints: 

142 # Can't check this, type annotation not supplied. 

143 return 

144 

145 possible_annotated_classes = _get_possible_annotated_classes( 

146 type_hints["return"] 

147 ) 

148 possible_returns = _get_valid_leave_annotations_for_classes( 

149 possible_match_classes 

150 ) 

151 

152 # Look at the union of specified return annotation, make sure that 

153 # they are all subclasses of the original leave_<Node> return 

154 # annotations. This catches when somebody tries to return a new node 

155 # that we know can't fit where the existing node was in the tree. 

156 for ret in possible_annotated_classes: 

157 for annotation in possible_returns: 

158 if issubclass(ret, annotation): 

159 # This annotation is a superclass of the possible match, 

160 # so we know that the types are correct. 

161 break 

162 else: 

163 # The current ret was not a subclass of any of the annotated 

164 # return types. 

165 raise MatchDecoratorMismatch( 

166 meth.__qualname__, 

167 f"@{decorator_name} decorated function cannot return " 

168 + f"the type {ret.__name__}.", 

169 ) 

170 

171 

172def _verify_parameter_annotations( 

173 possible_match_classes: Sequence[Type[cst.CSTNode]], 

174 # pyre-ignore We only care that meth is callable. 

175 meth: Callable[..., Any], 

176 decorator_name: str, 

177 *, 

178 expected_param_count: int, 

179) -> None: 

180 # First, verify that the number of parameters is sane. 

181 meth_signature = signature(meth) 

182 if len(meth_signature.parameters) != expected_param_count: 

183 raise MatchDecoratorMismatch( 

184 # pyre-fixme[16]: Anonymous callable has no attribute `__qualname__`. 

185 meth.__qualname__, 

186 f"@{decorator_name} should decorate functions which take " 

187 + f"{expected_param_count} parameter" 

188 + ("s" if expected_param_count > 1 else ""), 

189 ) 

190 

191 # Finally, for each parameter, make sure that the annotation includes 

192 # each of the classes that might appear given the match string. This 

193 # can be done in the simple case by just specifying the correct cst node 

194 # type. For complex matches that use OneOf/AllOf, this could be a base class 

195 # that encompases all possible matches, or a union. 

196 params = [v for k, v in get_type_hints(meth).items() if k != "return"] 

197 for param in params: 

198 # Go through each possible matcher, and make sure that the annotation 

199 # for types is a superclass of each matcher. 

200 possible_annotated_classes = _get_possible_annotated_classes(param) 

201 for match in possible_match_classes: 

202 for annotation in possible_annotated_classes: 

203 if issubclass(match, annotation): 

204 # This annotation is a superclass of the possible match, 

205 # so we know that the types are correct. 

206 break 

207 else: 

208 # The current match was not a subclass of any of the annotated 

209 # types. 

210 raise MatchDecoratorMismatch( 

211 meth.__qualname__, 

212 f"@{decorator_name} can be called with {match.__name__} " 

213 + "but the decorated function parameter annotations do " 

214 + "not include this type.", 

215 ) 

216 

217 

218def _check_types( 

219 # pyre-ignore We don't care about the type of sequence, just that its callable. 

220 decoratormap: Dict[BaseMatcherNode, Sequence[Callable[..., Any]]], 

221 decorator_name: str, 

222 *, 

223 expected_param_count: int, 

224 expected_none_return: bool, 

225) -> None: 

226 for matcher, methods in decoratormap.items(): 

227 # Given the matcher class we have, get the list of possible cst nodes that 

228 # could be passed to the functionis we wrap. 

229 possible_match_classes = _get_possible_match_classes(matcher) 

230 has_invalid_top_level = any( 

231 isinstance(m, (AtLeastN, AtMostN, MatchIfTrue)) 

232 for m in possible_match_classes 

233 ) 

234 

235 # Now, loop through each function we wrap and verify that the type signature 

236 # is valid. 

237 for meth in methods: 

238 # First thing first, make sure this isn't wrapping an inner class. 

239 if not ismethod(meth): 

240 raise MatchDecoratorMismatch( 

241 # pyre-fixme[16]: Anonymous callable has no attribute 

242 # `__qualname__`. 

243 meth.__qualname__, 

244 "Matcher decorators should only be used on methods of " 

245 + "MatcherDecoratableTransformer or " 

246 + "MatcherDecoratableVisitor", 

247 ) 

248 if has_invalid_top_level: 

249 raise MatchDecoratorMismatch( 

250 meth.__qualname__, 

251 "The root matcher in a matcher decorator cannot be an " 

252 + "AtLeastN, AtMostN or MatchIfTrue matcher", 

253 ) 

254 

255 # Now, check that the return annotation is valid. 

256 _verify_return_annotation( 

257 possible_match_classes, 

258 meth, 

259 decorator_name, 

260 expected_none=expected_none_return, 

261 ) 

262 

263 # Finally, check that the parameter annotations are valid. 

264 _verify_parameter_annotations( 

265 possible_match_classes, 

266 meth, 

267 decorator_name, 

268 expected_param_count=expected_param_count, 

269 ) 

270 

271 

272def _gather_matchers(obj: object) -> Set[BaseMatcherNode]: 

273 visit_matchers: Set[BaseMatcherNode] = set() 

274 

275 for func in dir(obj): 

276 try: 

277 for matcher in getattr(getattr(obj, func), VISIT_POSITIVE_MATCHER_ATTR, []): 

278 visit_matchers.add(cast(BaseMatcherNode, matcher)) 

279 for matcher in getattr(getattr(obj, func), VISIT_NEGATIVE_MATCHER_ATTR, []): 

280 visit_matchers.add(cast(BaseMatcherNode, matcher)) 

281 except Exception: 

282 # This could be a caculated property, and calling getattr() evaluates it. 

283 # We have no control over the implementation detail, so if it raises, we 

284 # should not crash. 

285 pass 

286 

287 return visit_matchers 

288 

289 

290def _assert_not_concrete( 

291 decorator_name: str, func: Callable[[cst.CSTNode], None] 

292) -> None: 

293 if func.__name__ in CONCRETE_METHODS: 

294 raise MatchDecoratorMismatch( 

295 # pyre-ignore This anonymous method has a qualname. 

296 func.__qualname__, 

297 f"@{decorator_name} should not decorate functions that are concrete " 

298 + "visit or leave methods.", 

299 ) 

300 

301 

302def _gather_constructed_visit_funcs( 

303 obj: object, 

304) -> Dict[BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]]]: 

305 constructed_visitors: Dict[ 

306 BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]] 

307 ] = {} 

308 

309 for funcname in dir(obj): 

310 try: 

311 possible_func = getattr(obj, funcname) 

312 if not ismethod(possible_func): 

313 continue 

314 func = cast(Callable[[cst.CSTNode], None], possible_func) 

315 except Exception: 

316 # This could be a caculated property, and calling getattr() evaluates it. 

317 # We have no control over the implementation detail, so if it raises, we 

318 # should not crash. 

319 continue 

320 matchers = getattr(func, CONSTRUCTED_VISIT_MATCHER_ATTR, []) 

321 if matchers: 

322 # Make sure that we aren't accidentally putting a @visit on a visit_Node. 

323 _assert_not_concrete("visit", func) 

324 for matcher in matchers: 

325 casted_matcher = cast(BaseMatcherNode, matcher) 

326 constructed_visitors[casted_matcher] = ( 

327 *constructed_visitors.get(casted_matcher, ()), 

328 func, 

329 ) 

330 

331 return constructed_visitors 

332 

333 

334# pyre-ignore: There is no reasonable way to type this, so ignore the Any type. This 

335# is because the leave_* methods have a different signature depending on whether they 

336# are in a MatcherDecoratableTransformer or a MatcherDecoratableVisitor. 

337def _gather_constructed_leave_funcs( 

338 obj: object, 

339) -> Dict[BaseMatcherNode, Sequence[Callable[..., Any]]]: 

340 constructed_visitors: Dict[ 

341 BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]] 

342 ] = {} 

343 

344 for funcname in dir(obj): 

345 try: 

346 possible_func = getattr(obj, funcname) 

347 if not ismethod(possible_func): 

348 continue 

349 func = cast(Callable[[cst.CSTNode], None], possible_func) 

350 except Exception: 

351 # This could be a caculated property, and calling getattr() evaluates it. 

352 # We have no control over the implementation detail, so if it raises, we 

353 # should not crash. 

354 continue 

355 matchers = getattr(func, CONSTRUCTED_LEAVE_MATCHER_ATTR, []) 

356 if matchers: 

357 # Make sure that we aren't accidentally putting a @leave on a leave_Node. 

358 _assert_not_concrete("leave", func) 

359 for matcher in matchers: 

360 casted_matcher = cast(BaseMatcherNode, matcher) 

361 constructed_visitors[casted_matcher] = ( 

362 *constructed_visitors.get(casted_matcher, ()), 

363 func, 

364 ) 

365 

366 return constructed_visitors 

367 

368 

369def _visit_matchers( 

370 matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], 

371 node: cst.CSTNode, 

372 metadata_resolver: cst.MetadataDependent, 

373) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]: 

374 new_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {} 

375 for matcher, existing_node in matchers.items(): 

376 # We don't care about visiting matchers that are already true. 

377 if existing_node is None and matches( 

378 node, matcher, metadata_resolver=metadata_resolver 

379 ): 

380 # This node matches! Remember which node it was so we can 

381 # cancel it later. 

382 new_matchers[matcher] = node 

383 else: 

384 new_matchers[matcher] = existing_node 

385 return new_matchers 

386 

387 

388def _leave_matchers( 

389 matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], node: cst.CSTNode 

390) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]: 

391 new_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {} 

392 for matcher, existing_node in matchers.items(): 

393 if node is existing_node: 

394 # This node matches, so we are no longer inside it. 

395 new_matchers[matcher] = None 

396 else: 

397 # We aren't leaving this node. 

398 new_matchers[matcher] = existing_node 

399 return new_matchers 

400 

401 

402def _all_positive_matchers_true( 

403 all_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], obj: object 

404) -> bool: 

405 requested_matchers = getattr(obj, VISIT_POSITIVE_MATCHER_ATTR, []) 

406 for matcher in requested_matchers: 

407 if all_matchers[matcher] is None: 

408 # The passed in object has been decorated with a matcher that isn't 

409 # active. 

410 return False 

411 return True 

412 

413 

414def _all_negative_matchers_false( 

415 all_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], obj: object 

416) -> bool: 

417 requested_matchers = getattr(obj, VISIT_NEGATIVE_MATCHER_ATTR, []) 

418 for matcher in requested_matchers: 

419 if all_matchers[matcher] is not None: 

420 # The passed in object has been decorated with a matcher that is active. 

421 return False 

422 return True 

423 

424 

425def _should_allow_visit( 

426 all_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], obj: object 

427) -> bool: 

428 return _all_positive_matchers_true( 

429 all_matchers, obj 

430 ) and _all_negative_matchers_false(all_matchers, obj) 

431 

432 

433def _visit_constructed_funcs( 

434 visit_funcs: Dict[BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]]], 

435 all_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]], 

436 node: cst.CSTNode, 

437 metadata_resolver: cst.MetadataDependent, 

438) -> None: 

439 for matcher, visit_funcs in visit_funcs.items(): 

440 if matches(node, matcher, metadata_resolver=metadata_resolver): 

441 for visit_func in visit_funcs: 

442 if _should_allow_visit(all_matchers, visit_func): 

443 visit_func(node) 

444 

445 

446class MatcherDecoratableTransformer(CSTTransformer): 

447 """ 

448 This class provides all of the features of a :class:`libcst.CSTTransformer`, and 

449 additionally supports various decorators to control when methods get called when 

450 traversing a tree. Use this instead of a :class:`libcst.CSTTransformer` if you 

451 wish to do more powerful decorator-based visiting. 

452 """ 

453 

454 def __init__(self) -> None: 

455 CSTTransformer.__init__(self) 

456 # List of gating matchers that we need to track and evaluate. We use these 

457 # in conjuction with the call_if_inside and call_if_not_inside decorators 

458 # to determine whether or not to call a visit/leave function. 

459 self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = { 

460 m: None for m in _gather_matchers(self) 

461 } 

462 # Mapping of matchers to functions. If in the course of visiting the tree, 

463 # a node matches one of these matchers, the corresponding function will be 

464 # called as if it was a visit_* method. 

465 self._extra_visit_funcs: Dict[ 

466 BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]] 

467 ] = _gather_constructed_visit_funcs(self) 

468 # Mapping of matchers to functions. If in the course of leaving the tree, 

469 # a node matches one of these matchers, the corresponding function will be 

470 # called as if it was a leave_* method. 

471 self._extra_leave_funcs: Dict[ 

472 BaseMatcherNode, 

473 Sequence[ 

474 Callable[ 

475 [cst.CSTNode, cst.CSTNode], Union[cst.CSTNode, cst.RemovalSentinel] 

476 ] 

477 ], 

478 ] = _gather_constructed_leave_funcs(self) 

479 # Make sure visit/leave functions constructed with @visit and @leave decorators 

480 # have correct type annotations. 

481 _check_types( 

482 self._extra_visit_funcs, 

483 "visit", 

484 expected_param_count=1, 

485 expected_none_return=True, 

486 ) 

487 _check_types( 

488 self._extra_leave_funcs, 

489 "leave", 

490 expected_param_count=2, 

491 expected_none_return=False, 

492 ) 

493 

494 def on_visit(self, node: cst.CSTNode) -> bool: 

495 # First, evaluate any matchers that we have which we are not inside already. 

496 self._matchers = _visit_matchers(self._matchers, node, self) 

497 

498 # Now, call any visitors that were hooked using a visit decorator. 

499 _visit_constructed_funcs(self._extra_visit_funcs, self._matchers, node, self) 

500 

501 # Now, evaluate whether this current function has any matchers it requires. 

502 if not _should_allow_visit( 

503 self._matchers, getattr(self, f"visit_{type(node).__name__}", None) 

504 ): 

505 # We shouldn't visit this directly. However, we should continue 

506 # visiting its children. 

507 return True 

508 

509 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

510 # matchers. In either case, just call the superclass behavior. 

511 return CSTTransformer.on_visit(self, node) 

512 

513 def on_leave( 

514 self, original_node: CSTNodeT, updated_node: CSTNodeT 

515 ) -> Union[CSTNodeT, cst.RemovalSentinel]: 

516 # First, evaluate whether this current function has a decorator on it. 

517 if _should_allow_visit( 

518 self._matchers, getattr(self, f"leave_{type(original_node).__name__}", None) 

519 ): 

520 retval = CSTTransformer.on_leave(self, original_node, updated_node) 

521 else: 

522 retval = updated_node 

523 

524 # Now, call any visitors that were hooked using a leave decorator. 

525 for matcher, leave_funcs in reversed(list(self._extra_leave_funcs.items())): 

526 if not self.matches(original_node, matcher): 

527 continue 

528 for leave_func in leave_funcs: 

529 if _should_allow_visit(self._matchers, leave_func) and isinstance( 

530 retval, cst.CSTNode 

531 ): 

532 retval = leave_func(original_node, retval) 

533 

534 # Now, see if we have any matchers we should deactivate. 

535 self._matchers = _leave_matchers(self._matchers, original_node) 

536 

537 # pyre-ignore The return value of on_leave is subtly wrong in that we can 

538 # actually return any value that passes this node's parent's constructor 

539 # validation. Fixing this is beyond the scope of this file, and would involve 

540 # forcing a lot of ensure_type() checks across the codebase. 

541 return retval 

542 

543 def on_visit_attribute(self, node: cst.CSTNode, attribute: str) -> None: 

544 # Evaluate whether this current function has a decorator on it. 

545 if _should_allow_visit( 

546 self._matchers, 

547 getattr(self, f"visit_{type(node).__name__}_{attribute}", None), 

548 ): 

549 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

550 # matchers. In either case, just call the superclass behavior. 

551 return CSTTransformer.on_visit_attribute(self, node, attribute) 

552 

553 def on_leave_attribute(self, original_node: cst.CSTNode, attribute: str) -> None: 

554 # Evaluate whether this current function has a decorator on it. 

555 if _should_allow_visit( 

556 self._matchers, 

557 getattr(self, f"leave_{type(original_node).__name__}_{attribute}", None), 

558 ): 

559 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

560 # matchers. In either case, just call the superclass behavior. 

561 CSTTransformer.on_leave_attribute(self, original_node, attribute) 

562 

563 def matches( 

564 self, 

565 node: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

566 matcher: BaseMatcherNode, 

567 ) -> bool: 

568 """ 

569 A convenience method to call :func:`~libcst.matchers.matches` without requiring 

570 an explicit parameter for metadata. Since our instance is an instance of 

571 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

572 documentation for :func:`~libcst.matchers.matches` as it is identical to this 

573 function. 

574 """ 

575 return matches(node, matcher, metadata_resolver=self) 

576 

577 def findall( 

578 self, 

579 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

580 matcher: Union[ 

581 BaseMatcherNode, 

582 MatchIfTrue[cst.CSTNode], 

583 MatchMetadata, 

584 MatchMetadataIfTrue, 

585 ], 

586 ) -> Sequence[cst.CSTNode]: 

587 """ 

588 A convenience method to call :func:`~libcst.matchers.findall` without requiring 

589 an explicit parameter for metadata. Since our instance is an instance of 

590 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

591 documentation for :func:`~libcst.matchers.findall` as it is identical to this 

592 function. 

593 """ 

594 return findall(tree, matcher, metadata_resolver=self) 

595 

596 def extract( 

597 self, 

598 node: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

599 matcher: BaseMatcherNode, 

600 ) -> Optional[Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]]: 

601 """ 

602 A convenience method to call :func:`~libcst.matchers.extract` without requiring 

603 an explicit parameter for metadata. Since our instance is an instance of 

604 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

605 documentation for :func:`~libcst.matchers.extract` as it is identical to this 

606 function. 

607 """ 

608 return extract(node, matcher, metadata_resolver=self) 

609 

610 def extractall( 

611 self, 

612 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

613 matcher: Union[ 

614 BaseMatcherNode, 

615 MatchIfTrue[cst.CSTNode], 

616 MatchMetadata, 

617 MatchMetadataIfTrue, 

618 ], 

619 ) -> Sequence[Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]]: 

620 """ 

621 A convenience method to call :func:`~libcst.matchers.extractall` without requiring 

622 an explicit parameter for metadata. Since our instance is an instance of 

623 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

624 documentation for :func:`~libcst.matchers.extractall` as it is identical to this 

625 function. 

626 """ 

627 return extractall(tree, matcher, metadata_resolver=self) 

628 

629 def replace( 

630 self, 

631 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

632 matcher: Union[ 

633 BaseMatcherNode, 

634 MatchIfTrue[cst.CSTNode], 

635 MatchMetadata, 

636 MatchMetadataIfTrue, 

637 ], 

638 replacement: Union[ 

639 cst.MaybeSentinel, 

640 cst.RemovalSentinel, 

641 cst.CSTNode, 

642 Callable[ 

643 [cst.CSTNode, Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]], 

644 Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

645 ], 

646 ], 

647 ) -> Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode]: 

648 """ 

649 A convenience method to call :func:`~libcst.matchers.replace` without requiring 

650 an explicit parameter for metadata. Since our instance is an instance of 

651 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

652 documentation for :func:`~libcst.matchers.replace` as it is identical to this 

653 function. 

654 """ 

655 return replace(tree, matcher, replacement, metadata_resolver=self) 

656 

657 

658class MatcherDecoratableVisitor(CSTVisitor): 

659 """ 

660 This class provides all of the features of a :class:`libcst.CSTVisitor`, and 

661 additionally supports various decorators to control when methods get called 

662 when traversing a tree. Use this instead of a :class:`libcst.CSTVisitor` if 

663 you wish to do more powerful decorator-based visiting. 

664 """ 

665 

666 def __init__(self) -> None: 

667 CSTVisitor.__init__(self) 

668 # List of gating matchers that we need to track and evaluate. We use these 

669 # in conjuction with the call_if_inside and call_if_not_inside decorators 

670 # to determine whether or not to call a visit/leave function. 

671 self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = { 

672 m: None for m in _gather_matchers(self) 

673 } 

674 # Mapping of matchers to functions. If in the course of visiting the tree, 

675 # a node matches one of these matchers, the corresponding function will be 

676 # called as if it was a visit_* method. 

677 self._extra_visit_funcs: Dict[ 

678 BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]] 

679 ] = _gather_constructed_visit_funcs(self) 

680 # Mapping of matchers to functions. If in the course of leaving the tree, 

681 # a node matches one of these matchers, the corresponding function will be 

682 # called as if it was a leave_* method. 

683 self._extra_leave_funcs: Dict[ 

684 BaseMatcherNode, Sequence[Callable[[cst.CSTNode], None]] 

685 ] = _gather_constructed_leave_funcs(self) 

686 # Make sure visit/leave functions constructed with @visit and @leave decorators 

687 # have correct type annotations. 

688 _check_types( 

689 self._extra_visit_funcs, 

690 "visit", 

691 expected_param_count=1, 

692 expected_none_return=True, 

693 ) 

694 _check_types( 

695 self._extra_leave_funcs, 

696 "leave", 

697 expected_param_count=1, 

698 expected_none_return=True, 

699 ) 

700 

701 def on_visit(self, node: cst.CSTNode) -> bool: 

702 # First, evaluate any matchers that we have which we are not inside already. 

703 self._matchers = _visit_matchers(self._matchers, node, self) 

704 

705 # Now, call any visitors that were hooked using a visit decorator. 

706 _visit_constructed_funcs(self._extra_visit_funcs, self._matchers, node, self) 

707 

708 # Now, evaluate whether this current function has a decorator on it. 

709 if not _should_allow_visit( 

710 self._matchers, getattr(self, f"visit_{type(node).__name__}", None) 

711 ): 

712 # We shouldn't visit this directly. However, we should continue 

713 # visiting its children. 

714 return True 

715 

716 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

717 # matchers. In either case, just call the superclass behavior. 

718 return CSTVisitor.on_visit(self, node) 

719 

720 def on_leave(self, original_node: cst.CSTNode) -> None: 

721 # First, evaluate whether this current function has a decorator on it. 

722 if _should_allow_visit( 

723 self._matchers, getattr(self, f"leave_{type(original_node).__name__}", None) 

724 ): 

725 CSTVisitor.on_leave(self, original_node) 

726 

727 # Now, call any visitors that were hooked using a leave decorator. 

728 for matcher, leave_funcs in reversed(list(self._extra_leave_funcs.items())): 

729 if not self.matches(original_node, matcher): 

730 continue 

731 for leave_func in leave_funcs: 

732 if _should_allow_visit(self._matchers, leave_func): 

733 leave_func(original_node) 

734 

735 # Now, see if we have any matchers we should deactivate. 

736 self._matchers = _leave_matchers(self._matchers, original_node) 

737 

738 def on_visit_attribute(self, node: cst.CSTNode, attribute: str) -> None: 

739 # Evaluate whether this current function has a decorator on it. 

740 if _should_allow_visit( 

741 self._matchers, 

742 getattr(self, f"visit_{type(node).__name__}_{attribute}", None), 

743 ): 

744 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

745 # matchers. In either case, just call the superclass behavior. 

746 return CSTVisitor.on_visit_attribute(self, node, attribute) 

747 

748 def on_leave_attribute(self, original_node: cst.CSTNode, attribute: str) -> None: 

749 # Evaluate whether this current function has a decorator on it. 

750 if _should_allow_visit( 

751 self._matchers, 

752 getattr(self, f"leave_{type(original_node).__name__}_{attribute}", None), 

753 ): 

754 # Either the visit_func doesn't exist, we have no matchers, or we passed all 

755 # matchers. In either case, just call the superclass behavior. 

756 CSTVisitor.on_leave_attribute(self, original_node, attribute) 

757 

758 def matches( 

759 self, 

760 node: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

761 matcher: BaseMatcherNode, 

762 ) -> bool: 

763 """ 

764 A convenience method to call :func:`~libcst.matchers.matches` without requiring 

765 an explicit parameter for metadata. Since our instance is an instance of 

766 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

767 documentation for :func:`~libcst.matchers.matches` as it is identical to this 

768 function. 

769 """ 

770 return matches(node, matcher, metadata_resolver=self) 

771 

772 def findall( 

773 self, 

774 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

775 matcher: Union[ 

776 BaseMatcherNode, 

777 MatchIfTrue[cst.CSTNode], 

778 MatchMetadata, 

779 MatchMetadataIfTrue, 

780 ], 

781 ) -> Sequence[cst.CSTNode]: 

782 """ 

783 A convenience method to call :func:`~libcst.matchers.findall` without requiring 

784 an explicit parameter for metadata. Since our instance is an instance of 

785 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

786 documentation for :func:`~libcst.matchers.findall` as it is identical to this 

787 function. 

788 """ 

789 return findall(tree, matcher, metadata_resolver=self) 

790 

791 def extract( 

792 self, 

793 node: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

794 matcher: BaseMatcherNode, 

795 ) -> Optional[Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]]: 

796 """ 

797 A convenience method to call :func:`~libcst.matchers.extract` without requiring 

798 an explicit parameter for metadata. Since our instance is an instance of 

799 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

800 documentation for :func:`~libcst.matchers.extract` as it is identical to this 

801 function. 

802 """ 

803 return extract(node, matcher, metadata_resolver=self) 

804 

805 def extractall( 

806 self, 

807 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

808 matcher: Union[ 

809 BaseMatcherNode, 

810 MatchIfTrue[cst.CSTNode], 

811 MatchMetadata, 

812 MatchMetadataIfTrue, 

813 ], 

814 ) -> Sequence[Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]]: 

815 """ 

816 A convenience method to call :func:`~libcst.matchers.extractall` without requiring 

817 an explicit parameter for metadata. Since our instance is an instance of 

818 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

819 documentation for :func:`~libcst.matchers.extractall` as it is identical to this 

820 function. 

821 """ 

822 return extractall(tree, matcher, metadata_resolver=self) 

823 

824 def replace( 

825 self, 

826 tree: Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

827 matcher: Union[ 

828 BaseMatcherNode, 

829 MatchIfTrue[cst.CSTNode], 

830 MatchMetadata, 

831 MatchMetadataIfTrue, 

832 ], 

833 replacement: Union[ 

834 cst.MaybeSentinel, 

835 cst.RemovalSentinel, 

836 cst.CSTNode, 

837 Callable[ 

838 [cst.CSTNode, Dict[str, Union[cst.CSTNode, Sequence[cst.CSTNode]]]], 

839 Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode], 

840 ], 

841 ], 

842 ) -> Union[cst.MaybeSentinel, cst.RemovalSentinel, cst.CSTNode]: 

843 """ 

844 A convenience method to call :func:`~libcst.matchers.replace` without requiring 

845 an explicit parameter for metadata. Since our instance is an instance of 

846 :class:`libcst.MetadataDependent`, we work as a metadata resolver. Please see 

847 documentation for :func:`~libcst.matchers.replace` as it is identical to this 

848 function. 

849 """ 

850 return replace(tree, matcher, replacement, metadata_resolver=self)