Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_nodes/module.py: 65%

57 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 dataclasses import dataclass 

7from typing import cast, Optional, Sequence, TYPE_CHECKING, TypeVar, Union 

8 

9from libcst._add_slots import add_slots 

10from libcst._nodes.base import CSTNode 

11from libcst._nodes.internal import CodegenState, visit_body_sequence, visit_sequence 

12from libcst._nodes.statement import ( 

13 BaseCompoundStatement, 

14 get_docstring_impl, 

15 SimpleStatementLine, 

16) 

17from libcst._nodes.whitespace import EmptyLine 

18from libcst._removal_sentinel import RemovalSentinel 

19from libcst._visitors import CSTVisitorT 

20 

21if TYPE_CHECKING: 

22 # This is circular, so import the type only in type checking 

23 from libcst._parser.types.config import PartialParserConfig 

24 

25 

26_ModuleSelfT = TypeVar("_ModuleSelfT", bound="Module") 

27 

28# type alias needed for scope overlap in type definition 

29builtin_bytes = bytes 

30 

31 

32@add_slots 

33@dataclass(frozen=True) 

34class Module(CSTNode): 

35 """ 

36 Contains some top-level information inferred from the file letting us set correct 

37 defaults when printing the tree about global formatting rules. All code parsed 

38 with :func:`parse_module` will be encapsulated in a module. 

39 """ 

40 

41 #: A list of zero or more statements that make up this module. 

42 body: Sequence[Union[SimpleStatementLine, BaseCompoundStatement]] 

43 

44 #: Normally any whitespace/comments are assigned to the next node visited, but 

45 #: :class:`Module` is a special case, and comments at the top of the file tend 

46 #: to refer to the module itself, so we assign them to the :class:`Module` 

47 #: instead of the first statement in the body. 

48 header: Sequence[EmptyLine] = () 

49 

50 #: Any trailing whitespace/comments found after the last statement. 

51 footer: Sequence[EmptyLine] = () 

52 

53 #: The file's encoding format. When parsing a ``bytes`` object, this value may be 

54 #: inferred from the contents of the parsed source code. When parsing a ``str``, 

55 #: this value defaults to ``"utf-8"``. 

56 #: 

57 #: This value affects how :attr:`bytes` encodes the source code. 

58 encoding: str = "utf-8" 

59 

60 #: The indentation of the file, expressed as a series of tabs and/or spaces. This 

61 #: value is inferred from the contents of the parsed source code by default. 

62 default_indent: str = " " * 4 

63 

64 #: The newline of the file, expressed as ``\n``, ``\r\n``, or ``\r``. This value is 

65 #: inferred from the contents of the parsed source code by default. 

66 default_newline: str = "\n" 

67 

68 #: Whether the module has a trailing newline or not. 

69 has_trailing_newline: bool = True 

70 

71 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Module": 

72 return Module( 

73 header=visit_sequence(self, "header", self.header, visitor), 

74 body=visit_body_sequence(self, "body", self.body, visitor), 

75 footer=visit_sequence(self, "footer", self.footer, visitor), 

76 encoding=self.encoding, 

77 default_indent=self.default_indent, 

78 default_newline=self.default_newline, 

79 has_trailing_newline=self.has_trailing_newline, 

80 ) 

81 

82 # pyre-fixme[14]: `visit` overrides method defined in `CSTNode` inconsistently. 

83 def visit(self: _ModuleSelfT, visitor: CSTVisitorT) -> _ModuleSelfT: 

84 """ 

85 Returns the result of running a visitor over this module. 

86 

87 :class:`Module` overrides the default visitor entry point to resolve metadata 

88 dependencies declared by 'visitor'. 

89 """ 

90 result = super(Module, self).visit(visitor) 

91 if isinstance(result, RemovalSentinel): 

92 return self.with_changes(body=(), header=(), footer=()) 

93 else: # is a Module 

94 return cast(_ModuleSelfT, result) 

95 

96 def _codegen_impl(self, state: CodegenState) -> None: 

97 for h in self.header: 

98 h._codegen(state) 

99 for stmt in self.body: 

100 stmt._codegen(state) 

101 for f in self.footer: 

102 f._codegen(state) 

103 if self.has_trailing_newline: 

104 if len(state.tokens) == 0: 

105 # There was nothing in the header, footer, or body. Just add a newline 

106 # to preserve the trailing newline. 

107 state.add_token(state.default_newline) 

108 else: # has_trailing_newline is false 

109 state.pop_trailing_newline() 

110 

111 @property 

112 def code(self) -> str: 

113 """ 

114 The string representation of this module, respecting the inferred indentation 

115 and newline type. 

116 """ 

117 return self.code_for_node(self) 

118 

119 @property 

120 def bytes(self) -> builtin_bytes: 

121 """ 

122 The bytes representation of this module, respecting the inferred indentation 

123 and newline type, using the current encoding. 

124 """ 

125 return self.code.encode(self.encoding) 

126 

127 def code_for_node(self, node: CSTNode) -> str: 

128 """ 

129 Generates the code for the given node in the context of this module. This is a 

130 method of Module, not CSTNode, because we need to know the module's default 

131 indentation and newline formats. 

132 """ 

133 

134 state = CodegenState( 

135 default_indent=self.default_indent, default_newline=self.default_newline 

136 ) 

137 node._codegen(state) 

138 return "".join(state.tokens) 

139 

140 @property 

141 def config_for_parsing(self) -> "PartialParserConfig": 

142 """ 

143 Generates a parser config appropriate for passing to a :func:`parse_expression` 

144 or :func:`parse_statement` call. This is useful when using either parser 

145 function to generate code from a string template. By using a generated parser 

146 config instead of the default, you can guarantee that trees generated from 

147 both statement and expression strings have the same inferred defaults for things 

148 like newlines, indents and similar:: 

149 

150 module = cst.parse_module("pass\\n") 

151 expression = cst.parse_expression("1 + 2", config=module.config_for_parsing) 

152 """ 

153 

154 from libcst._parser.types.config import PartialParserConfig 

155 

156 return PartialParserConfig( 

157 encoding=self.encoding, 

158 default_indent=self.default_indent, 

159 default_newline=self.default_newline, 

160 ) 

161 

162 def get_docstring(self, clean: bool = True) -> Optional[str]: 

163 """ 

164 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise. 

165 """ 

166 return get_docstring_impl(self.body, clean)