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
« 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.
6from dataclasses import dataclass
7from typing import cast, Optional, Sequence, TYPE_CHECKING, TypeVar, Union
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
21if TYPE_CHECKING:
22 # This is circular, so import the type only in type checking
23 from libcst._parser.types.config import PartialParserConfig
26_ModuleSelfT = TypeVar("_ModuleSelfT", bound="Module")
28# type alias needed for scope overlap in type definition
29builtin_bytes = bytes
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 """
41 #: A list of zero or more statements that make up this module.
42 body: Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]
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] = ()
50 #: Any trailing whitespace/comments found after the last statement.
51 footer: Sequence[EmptyLine] = ()
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"
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
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"
68 #: Whether the module has a trailing newline or not.
69 has_trailing_newline: bool = True
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 )
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.
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)
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()
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)
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)
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 """
134 state = CodegenState(
135 default_indent=self.default_indent, default_newline=self.default_newline
136 )
137 node._codegen(state)
138 return "".join(state.tokens)
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::
150 module = cst.parse_module("pass\\n")
151 expression = cst.parse_expression("1 + 2", config=module.config_for_parsing)
152 """
154 from libcst._parser.types.config import PartialParserConfig
156 return PartialParserConfig(
157 encoding=self.encoding,
158 default_indent=self.default_indent,
159 default_newline=self.default_newline,
160 )
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)