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

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 

4 

5"""This module contains some base nodes that can be inherited for the different nodes. 

6 

7Previously these were called Mixin nodes. 

8""" 

9 

10from __future__ import annotations 

11 

12import itertools 

13from collections.abc import Iterator 

14from functools import cached_property 

15from typing import TYPE_CHECKING, ClassVar 

16 

17from astroid.exceptions import AttributeInferenceError 

18from astroid.nodes.node_ng import NodeNG 

19 

20if TYPE_CHECKING: 

21 from astroid import nodes 

22 

23 

24class Statement(NodeNG): 

25 """Statement node adding a few attributes. 

26 

27 NOTE: This class is part of the public API of 'astroid.nodes'. 

28 """ 

29 

30 is_statement = True 

31 """Whether this node indicates a statement.""" 

32 

33 def next_sibling(self): 

34 """The next sibling statement node. 

35 

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 

45 

46 def previous_sibling(self): 

47 """The previous sibling statement. 

48 

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 

57 

58 

59class NoChildrenNode(NodeNG): 

60 """Base nodes for nodes with no children, e.g. Pass.""" 

61 

62 def get_children(self) -> Iterator[NodeNG]: 

63 yield from () 

64 

65 

66class FilterStmtsBaseNode(NodeNG): 

67 """Base node for statement filtering and assignment type.""" 

68 

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 

76 

77 def assign_type(self): 

78 return self 

79 

80 

81class AssignTypeNode(NodeNG): 

82 """Base node for nodes that can 'assign' such as AnnAssign.""" 

83 

84 def assign_type(self): 

85 return self 

86 

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 

96 

97 

98class ParentAssignNode(AssignTypeNode): 

99 """Base node for nodes whose assign_type is determined by the parent node.""" 

100 

101 def assign_type(self): 

102 return self.parent.assign_type() 

103 

104 

105class ImportNode(FilterStmtsBaseNode, NoChildrenNode, Statement): 

106 """Base node for From and Import Nodes.""" 

107 

108 modname: str | None 

109 """The module that is being imported from. 

110 

111 This is ``None`` for relative imports. 

112 """ 

113 

114 names: list[tuple[str, str | None]] 

115 """What is being imported from the module. 

116 

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 """ 

120 

121 def _infer_name(self, frame, name): 

122 return name 

123 

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 

138 

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 ) 

146 

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 ) 

162 

163 

164class MultiLineBlockNode(NodeNG): 

165 """Base node for multi-line blocks, e.g. For and FunctionDef. 

166 

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 """ 

172 

173 _multi_line_block_fields: ClassVar[tuple[str, ...]] = () 

174 

175 @cached_property 

176 def _multi_line_blocks(self): 

177 return tuple(getattr(self, field) for field in self._multi_line_block_fields) 

178 

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() 

185 

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() 

192 

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)) 

201 

202 

203class MultiLineWithElseBlockNode(MultiLineBlockNode): 

204 """Base node for multi-line blocks that can have else statements.""" 

205 

206 @cached_property 

207 def blockstart_tolineno(self): 

208 return self.lineno 

209 

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