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

41 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# 

6 

7""" 

8Provides everything needed to run a CodemodCommand. 

9""" 

10 

11import traceback 

12from dataclasses import dataclass 

13from enum import Enum 

14from typing import Optional, Sequence, Union 

15 

16from libcst import parse_module, PartialParserConfig 

17from libcst.codemod._codemod import Codemod 

18 

19# All datastructures defined in this class are pickleable so that they can be used 

20# as a return value with the multiprocessing module. 

21 

22 

23@dataclass(frozen=True) 

24class TransformSuccess: 

25 """ 

26 A :class:`~libcst.codemod.TransformResult` used when the codemod was successful. 

27 Stores all the information we might need to display to the user upon success, as 

28 well as the transformed file contents. 

29 """ 

30 

31 #: All warning messages that were generated during the codemod. 

32 warning_messages: Sequence[str] 

33 

34 #: The updated code, post-codemod. 

35 code: str 

36 

37 

38@dataclass(frozen=True) 

39class TransformFailure: 

40 """ 

41 A :class:`~libcst.codemod.TransformResult` used when the codemod failed. 

42 Stores all the information we might need to display to the user upon a failure. 

43 """ 

44 

45 #: All warning messages that were generated before the codemod crashed. 

46 warning_messages: Sequence[str] 

47 

48 #: The exception that was raised during the codemod. 

49 error: Exception 

50 

51 #: The traceback string that was recorded at the time of exception. 

52 traceback_str: str 

53 

54 

55@dataclass(frozen=True) 

56class TransformExit: 

57 """ 

58 A :class:`~libcst.codemod.TransformResult` used when the codemod was interrupted 

59 by the user (e.g. KeyboardInterrupt). 

60 """ 

61 

62 #: An empty list of warnings, included so that all 

63 #: :class:`~libcst.codemod.TransformResult` have a ``warning_messages`` attribute. 

64 warning_messages: Sequence[str] = () 

65 

66 

67class SkipReason(Enum): 

68 """ 

69 An enumeration of all valid reasons for a codemod to skip. 

70 """ 

71 

72 #: The module was skipped because we detected that it was generated code, and 

73 #: we were configured to skip generated files. 

74 GENERATED = "generated" 

75 

76 #: The module was skipped because we detected that it was blacklisted, and we 

77 #: were configured to skip blacklisted files. 

78 BLACKLISTED = "blacklisted" 

79 

80 #: The module was skipped because the codemod requested us to skip using the 

81 #: :class:`~libcst.codemod.SkipFile` exception. 

82 OTHER = "other" 

83 

84 

85@dataclass(frozen=True) 

86class TransformSkip: 

87 """ 

88 A :class:`~libcst.codemod.TransformResult` used when the codemod requested to 

89 be skipped. This could be because it's a generated file, or due to filename 

90 blacklist, or because the transform raised :class:`~libcst.codemod.SkipFile`. 

91 """ 

92 

93 #: The reason that we skipped codemodding this module. 

94 skip_reason: SkipReason 

95 

96 #: The description populated from the :class:`~libcst.codemod.SkipFile` exception. 

97 skip_description: str 

98 

99 #: All warning messages that were generated before the codemod decided to skip. 

100 warning_messages: Sequence[str] = () 

101 

102 

103class SkipFile(Exception): 

104 """ 

105 Raise this exception to skip codemodding the current file. 

106 

107 The exception message should be the reason for skipping. 

108 """ 

109 

110 

111TransformResult = Union[ 

112 TransformSuccess, TransformFailure, TransformExit, TransformSkip 

113] 

114 

115 

116def transform_module( 

117 transformer: Codemod, code: str, *, python_version: Optional[str] = None 

118) -> TransformResult: 

119 """ 

120 Given a module as represented by a string and a :class:`~libcst.codemod.Codemod` 

121 that we wish to run, execute the codemod on the code and return a 

122 :class:`~libcst.codemod.TransformResult`. This should never raise an exception. 

123 On success, this returns a :class:`~libcst.codemod.TransformSuccess` containing 

124 any generated warnings as well as the transformed code. If the codemod is 

125 interrupted with a Ctrl+C, this returns a :class:`~libcst.codemod.TransformExit`. 

126 If the codemod elected to skip by throwing a :class:`~libcst.codemod.SkipFile` 

127 exception, this will return a :class:`~libcst.codemod.TransformSkip` containing 

128 the reason for skipping as well as any warnings that were generated before 

129 the codemod decided to skip. If the codemod throws an unexpected exception, 

130 this will return a :class:`~libcst.codemod.TransformFailure` containing the 

131 exception that occured as well as any warnings that were generated before the 

132 codemod crashed. 

133 """ 

134 try: 

135 input_tree = parse_module( 

136 code, 

137 config=( 

138 PartialParserConfig(python_version=python_version) 

139 if python_version is not None 

140 else PartialParserConfig() 

141 ), 

142 ) 

143 output_tree = transformer.transform_module(input_tree) 

144 return TransformSuccess( 

145 code=output_tree.code, warning_messages=transformer.context.warnings 

146 ) 

147 except KeyboardInterrupt: 

148 return TransformExit() 

149 except SkipFile as ex: 

150 return TransformSkip( 

151 skip_description=str(ex), 

152 skip_reason=SkipReason.OTHER, 

153 warning_messages=transformer.context.warnings, 

154 ) 

155 except Exception as ex: 

156 return TransformFailure( 

157 error=ex, 

158 traceback_str=traceback.format_exc(), 

159 warning_messages=transformer.context.warnings, 

160 )