Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/libcst/_nodes/module.py: 64%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 def visit(self: _ModuleSelfT, visitor: CSTVisitorT) -> _ModuleSelfT:
83 """
84 Returns the result of running a visitor over this module.
86 :class:`Module` overrides the default visitor entry point to resolve metadata
87 dependencies declared by 'visitor'.
88 """
89 result = super(Module, self).visit(visitor)
90 if isinstance(result, RemovalSentinel):
91 return self.with_changes(body=(), header=(), footer=())
92 else: # is a Module
93 return cast(_ModuleSelfT, result)
95 def _codegen_impl(self, state: CodegenState) -> None:
96 for h in self.header:
97 h._codegen(state)
98 for stmt in self.body:
99 stmt._codegen(state)
100 for f in self.footer:
101 f._codegen(state)
102 if self.has_trailing_newline:
103 if len(state.tokens) == 0:
104 # There was nothing in the header, footer, or body. Just add a newline
105 # to preserve the trailing newline.
106 state.add_token(state.default_newline)
107 else: # has_trailing_newline is false
108 state.pop_trailing_newline()
110 @property
111 def code(self) -> str:
112 """
113 The string representation of this module, respecting the inferred indentation
114 and newline type.
115 """
116 return self.code_for_node(self)
118 @property
119 def bytes(self) -> builtin_bytes:
120 """
121 The bytes representation of this module, respecting the inferred indentation
122 and newline type, using the current encoding.
123 """
124 return self.code.encode(self.encoding)
126 def code_for_node(self, node: CSTNode) -> str:
127 """
128 Generates the code for the given node in the context of this module. This is a
129 method of Module, not CSTNode, because we need to know the module's default
130 indentation and newline formats.
131 """
133 state = CodegenState(
134 default_indent=self.default_indent, default_newline=self.default_newline
135 )
136 node._codegen(state)
137 return "".join(state.tokens)
139 @property
140 def config_for_parsing(self) -> "PartialParserConfig":
141 """
142 Generates a parser config appropriate for passing to a :func:`parse_expression`
143 or :func:`parse_statement` call. This is useful when using either parser
144 function to generate code from a string template. By using a generated parser
145 config instead of the default, you can guarantee that trees generated from
146 both statement and expression strings have the same inferred defaults for things
147 like newlines, indents and similar::
149 module = cst.parse_module("pass\\n")
150 expression = cst.parse_expression("1 + 2", config=module.config_for_parsing)
151 """
153 from libcst._parser.types.config import PartialParserConfig
155 return PartialParserConfig(
156 encoding=self.encoding,
157 default_indent=self.default_indent,
158 default_newline=self.default_newline,
159 )
161 def get_docstring(self, clean: bool = True) -> Optional[str]:
162 """
163 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise.
164 """
165 return get_docstring_impl(self.body, clean)