Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/nodes/_base_nodes.py: 57%
104 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""This module contains some base nodes that can be inherited for the different nodes.
7Previously these were called Mixin nodes.
8"""
10from __future__ import annotations
12import itertools
13from collections.abc import Iterator
14from functools import cached_property
15from typing import TYPE_CHECKING, ClassVar
17from astroid.exceptions import AttributeInferenceError
18from astroid.nodes.node_ng import NodeNG
20if TYPE_CHECKING:
21 from astroid import nodes
24class Statement(NodeNG):
25 """Statement node adding a few attributes.
27 NOTE: This class is part of the public API of 'astroid.nodes'.
28 """
30 is_statement = True
31 """Whether this node indicates a statement."""
33 def next_sibling(self):
34 """The next sibling statement node.
36 :returns: The next sibling statement node.
37 :rtype: NodeNG or None
38 """
39 stmts = self.parent.child_sequence(self)
40 index = stmts.index(self)
41 try:
42 return stmts[index + 1]
43 except IndexError:
44 return None
46 def previous_sibling(self):
47 """The previous sibling statement.
49 :returns: The previous sibling statement node.
50 :rtype: NodeNG or None
51 """
52 stmts = self.parent.child_sequence(self)
53 index = stmts.index(self)
54 if index >= 1:
55 return stmts[index - 1]
56 return None
59class NoChildrenNode(NodeNG):
60 """Base nodes for nodes with no children, e.g. Pass."""
62 def get_children(self) -> Iterator[NodeNG]:
63 yield from ()
66class FilterStmtsBaseNode(NodeNG):
67 """Base node for statement filtering and assignment type."""
69 def _get_filtered_stmts(self, _, node, _stmts, mystmt: Statement | None):
70 """Method used in _filter_stmts to get statements and trigger break."""
71 if self.statement(future=True) is mystmt:
72 # original node's statement is the assignment, only keep
73 # current node (gen exp, list comp)
74 return [node], True
75 return _stmts, False
77 def assign_type(self):
78 return self
81class AssignTypeNode(NodeNG):
82 """Base node for nodes that can 'assign' such as AnnAssign."""
84 def assign_type(self):
85 return self
87 def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt: Statement | None):
88 """Method used in filter_stmts."""
89 if self is mystmt:
90 return _stmts, True
91 if self.statement(future=True) is mystmt:
92 # original node's statement is the assignment, only keep
93 # current node (gen exp, list comp)
94 return [node], True
95 return _stmts, False
98class ParentAssignNode(AssignTypeNode):
99 """Base node for nodes whose assign_type is determined by the parent node."""
101 def assign_type(self):
102 return self.parent.assign_type()
105class ImportNode(FilterStmtsBaseNode, NoChildrenNode, Statement):
106 """Base node for From and Import Nodes."""
108 modname: str | None
109 """The module that is being imported from.
111 This is ``None`` for relative imports.
112 """
114 names: list[tuple[str, str | None]]
115 """What is being imported from the module.
117 Each entry is a :class:`tuple` of the name being imported,
118 and the alias that the name is assigned to (if any).
119 """
121 def _infer_name(self, frame, name):
122 return name
124 def do_import_module(self, modname: str | None = None) -> nodes.Module:
125 """Return the ast for a module whose name is <modname> imported by <self>."""
126 mymodule = self.root()
127 level: int | None = getattr(self, "level", None) # Import has no level
128 if modname is None:
129 modname = self.modname
130 # If the module ImportNode is importing is a module with the same name
131 # as the file that contains the ImportNode we don't want to use the cache
132 # to make sure we use the import system to get the correct module.
133 # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule
134 if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
135 use_cache = False
136 else:
137 use_cache = True
139 # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule
140 return mymodule.import_module(
141 modname,
142 level=level,
143 relative_only=bool(level and level >= 1),
144 use_cache=use_cache,
145 )
147 def real_name(self, asname: str) -> str:
148 """Get name from 'as' name."""
149 for name, _asname in self.names:
150 if name == "*":
151 return asname
152 if not _asname:
153 name = name.split(".", 1)[0]
154 _asname = name
155 if asname == _asname:
156 return name
157 raise AttributeInferenceError(
158 "Could not find original name for {attribute} in {target!r}",
159 target=self,
160 attribute=asname,
161 )
164class MultiLineBlockNode(NodeNG):
165 """Base node for multi-line blocks, e.g. For and FunctionDef.
167 Note that this does not apply to every node with a `body` field.
168 For instance, an If node has a multi-line body, but the body of an
169 IfExpr is not multi-line, and hence cannot contain Return nodes,
170 Assign nodes, etc.
171 """
173 _multi_line_block_fields: ClassVar[tuple[str, ...]] = ()
175 @cached_property
176 def _multi_line_blocks(self):
177 return tuple(getattr(self, field) for field in self._multi_line_block_fields)
179 def _get_return_nodes_skip_functions(self):
180 for block in self._multi_line_blocks:
181 for child_node in block:
182 if child_node.is_function:
183 continue
184 yield from child_node._get_return_nodes_skip_functions()
186 def _get_yield_nodes_skip_lambdas(self):
187 for block in self._multi_line_blocks:
188 for child_node in block:
189 if child_node.is_lambda:
190 continue
191 yield from child_node._get_yield_nodes_skip_lambdas()
193 @cached_property
194 def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
195 children_assign_nodes = (
196 child_node._assign_nodes_in_scope
197 for block in self._multi_line_blocks
198 for child_node in block
199 )
200 return list(itertools.chain.from_iterable(children_assign_nodes))
203class MultiLineWithElseBlockNode(MultiLineBlockNode):
204 """Base node for multi-line blocks that can have else statements."""
206 @cached_property
207 def blockstart_tolineno(self):
208 return self.lineno
210 def _elsed_block_range(
211 self, lineno: int, orelse: list[nodes.NodeNG], last: int | None = None
212 ) -> tuple[int, int]:
213 """Handle block line numbers range for try/finally, for, if and while
214 statements.
215 """
216 if lineno == self.fromlineno:
217 return lineno, lineno
218 if orelse:
219 if lineno >= orelse[0].fromlineno:
220 return lineno, orelse[-1].tolineno
221 return lineno, orelse[0].fromlineno - 1
222 return lineno, last or self.tolineno