Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/codemod/_visitor.py: 51%

35 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 typing import Mapping 

7 

8import libcst as cst 

9from libcst import MetadataDependent 

10from libcst.codemod._codemod import Codemod 

11from libcst.codemod._context import CodemodContext 

12from libcst.matchers import MatcherDecoratableTransformer, MatcherDecoratableVisitor 

13from libcst.metadata import ProviderT 

14 

15 

16class ContextAwareTransformer(Codemod, MatcherDecoratableTransformer): 

17 """ 

18 A transformer which visits using LibCST. Allows visitor-based mutation of a tree. 

19 Classes wishing to do arbitrary non-visitor-based mutation on a tree should 

20 instead subclass from :class:`Codemod` and implement 

21 :meth:`~Codemod.transform_module_impl`. This is a subclass of 

22 :class:`~libcst.matchers.MatcherDecoratableTransformer` so all features of matchers 

23 as well as :class:`~libcst.CSTTransformer` are available to subclasses of this 

24 class. 

25 """ 

26 

27 def __init__(self, context: CodemodContext) -> None: 

28 Codemod.__init__(self, context) 

29 MatcherDecoratableTransformer.__init__(self) 

30 

31 def transform_module_impl(self, tree: cst.Module) -> cst.Module: 

32 return tree.visit(self) 

33 

34 

35class ContextAwareVisitor(MatcherDecoratableVisitor, MetadataDependent): 

36 """ 

37 A visitor which visits using LibCST. Allows visitor-based collecting of info 

38 on a tree. All codemods which wish to implement an information collector should 

39 subclass from this instead of directly from 

40 :class:`~libcst.matchers.MatcherDecoratableVisitor` or :class:`~libcst.CSTVisitor` 

41 since this provides access to the current codemod context. As a result, this 

42 class allows access to metadata which was calculated in a parent 

43 :class:`~libcst.codemod.Codemod` through the 

44 :meth:`~libcst.MetadataDependent.get_metadata` method. 

45 

46 Note that you cannot directly run a :class:`~libcst.codemod.ContextAwareVisitor` 

47 using :func:`~libcst.codemod.transform_module` because visitors by definition 

48 do not transform trees. However, you can instantiate a 

49 :class:`~libcst.codemod.ContextAwareVisitor` inside a codemod and pass it to the 

50 :class:`~libcst.CSTNode.visit` method on any node in order to run information 

51 gathering with metadata and context support. 

52 

53 Remember that a :class:`~libcst.codemod.ContextAwareVisitor` is a subclass of 

54 :class:`~libcst.MetadataDependent`, meaning that you still need to declare 

55 your metadata dependencies with 

56 :attr:`~libcst.MetadataDependent.METADATA_DEPENDENCIES` before you can retrieve 

57 metadata using :meth:`~libcst.MetadataDependent.get_metadata`, even if the parent 

58 codemod has listed its own metadata dependencies. Note also that the dependencies 

59 listed on this class must be a strict subset of the dependencies listed in the 

60 parent codemod. 

61 """ 

62 

63 def __init__(self, context: CodemodContext) -> None: 

64 MetadataDependent.__init__(self) 

65 MatcherDecoratableVisitor.__init__(self) 

66 self.context = context 

67 

68 dependencies = self.get_inherited_dependencies() 

69 if dependencies: 

70 wrapper = self.context.wrapper 

71 if wrapper is None: 

72 raise Exception( 

73 f"Attempting to instantiate {self.__class__.__name__} outside of " 

74 + "an active transform. This means that metadata hasn't been " 

75 + "calculated and we cannot successfully create this visitor." 

76 ) 

77 for dep in dependencies: 

78 if dep not in wrapper._metadata: 

79 raise Exception( 

80 f"Attempting to access metadata {dep.__name__} that was not a " 

81 + "declared dependency of parent transform! This means it is " 

82 + "not possible to compute this value. Please ensure that all " 

83 + f"parent transforms of {self.__class__.__name__} declare " 

84 + f"{dep.__name__} as a metadata dependency." 

85 ) 

86 self.metadata: Mapping[ProviderT, Mapping[cst.CSTNode, object]] = { 

87 dep: wrapper._metadata[dep] for dep in dependencies 

88 } 

89 

90 def warn(self, warning: str) -> None: 

91 """ 

92 Emit a warning that is displayed to the user who has invoked this codemod. 

93 """ 

94 self.context.warnings.append(warning) 

95 

96 @property 

97 def module(self) -> cst.Module: 

98 """ 

99 Reference to the currently-traversed module. Note that this is only available 

100 during a transform itself. 

101 """ 

102 module = self.context.module 

103 if module is None: 

104 raise Exception( 

105 f"Attempted access of {self.__class__.__name__}.module outside of " 

106 + "transform_module()." 

107 ) 

108 return module