Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py: 60%

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

68 statements  

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 mixin classes for scoped nodes.""" 

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING, TypeVar, overload 

10 

11from astroid.exceptions import ParentMissingError 

12from astroid.filter_statements import _filter_stmts 

13from astroid.nodes import _base_nodes, scoped_nodes 

14from astroid.nodes.scoped_nodes.utils import builtin_lookup 

15from astroid.typing import InferenceResult, SuccessfulInferenceResult 

16 

17if TYPE_CHECKING: 

18 from astroid import nodes 

19 

20_T = TypeVar("_T") 

21 

22 

23class LocalsDictNodeNG(_base_nodes.LookupMixIn): 

24 """this class provides locals handling common to Module, FunctionDef 

25 and ClassDef nodes, including a dict like interface for direct access 

26 to locals information 

27 """ 

28 

29 # attributes below are set by the builder module or by raw factories 

30 locals: dict[str, list[InferenceResult]] 

31 """A map of the name of a local variable to the node defining the local.""" 

32 

33 def qname(self) -> str: 

34 """Get the 'qualified' name of the node. 

35 

36 For example: module.name, module.class.name ... 

37 

38 :returns: The qualified name. 

39 :rtype: str 

40 """ 

41 # pylint: disable=no-member; github.com/pylint-dev/astroid/issues/278 

42 if self.parent is None: 

43 return self.name 

44 try: 

45 return f"{self.parent.frame().qname()}.{self.name}" 

46 except ParentMissingError: 

47 return self.name 

48 

49 def scope(self: _T) -> _T: 

50 """The first parent node defining a new scope. 

51 

52 :returns: The first parent scope node. 

53 :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr 

54 """ 

55 return self 

56 

57 def scope_lookup( 

58 self, node: _base_nodes.LookupMixIn, name: str, offset: int = 0 

59 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

60 """Lookup where the given variable is assigned. 

61 

62 :param node: The node to look for assignments up to. 

63 Any assignments after the given node are ignored. 

64 

65 :param name: The name of the variable to find assignments for. 

66 

67 :param offset: The line offset to filter statements up to. 

68 

69 :returns: This scope node and the list of assignments associated to the 

70 given name according to the scope where it has been found (locals, 

71 globals or builtin). 

72 """ 

73 raise NotImplementedError 

74 

75 def _scope_lookup( 

76 self, node: _base_nodes.LookupMixIn, name: str, offset: int = 0 

77 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

78 """XXX method for interfacing the scope lookup""" 

79 try: 

80 stmts = _filter_stmts(node, self.locals[name], self, offset) 

81 except KeyError: 

82 stmts = () 

83 if stmts: 

84 return self, stmts 

85 

86 # Handle nested scopes: since class names do not extend to nested 

87 # scopes (e.g., methods), we find the next enclosing non-class scope 

88 pscope = self.parent and self.parent.scope() 

89 while pscope is not None: 

90 if not isinstance(pscope, scoped_nodes.ClassDef): 

91 return pscope.scope_lookup(node, name) 

92 pscope = pscope.parent and pscope.parent.scope() 

93 

94 # self is at the top level of a module, or is enclosed only by ClassDefs 

95 return builtin_lookup(name) 

96 

97 def set_local(self, name: str, stmt: nodes.NodeNG) -> None: 

98 """Define that the given name is declared in the given statement node. 

99 

100 .. seealso:: :meth:`scope` 

101 

102 :param name: The name that is being defined. 

103 

104 :param stmt: The statement that defines the given name. 

105 """ 

106 # assert not stmt in self.locals.get(name, ()), (self, stmt) 

107 self.locals.setdefault(name, []).append(stmt) 

108 

109 __setitem__ = set_local 

110 

111 def _append_node(self, child: nodes.NodeNG) -> None: 

112 """append a child, linking it in the tree""" 

113 # pylint: disable=no-member; depending by the class 

114 # which uses the current class as a mixin or base class. 

115 # It's rewritten in 2.0, so it makes no sense for now 

116 # to spend development time on it. 

117 self.body.append(child) # type: ignore[attr-defined] 

118 child.parent = self 

119 

120 @overload 

121 def add_local_node( 

122 self, child_node: nodes.ClassDef, name: str | None = ... 

123 ) -> None: ... 

124 

125 @overload 

126 def add_local_node(self, child_node: nodes.NodeNG, name: str) -> None: ... 

127 

128 def add_local_node(self, child_node: nodes.NodeNG, name: str | None = None) -> None: 

129 """Append a child that should alter the locals of this scope node. 

130 

131 :param child_node: The child node that will alter locals. 

132 

133 :param name: The name of the local that will be altered by 

134 the given child node. 

135 """ 

136 if name != "__class__": 

137 # add __class__ node as a child will cause infinite recursion later! 

138 self._append_node(child_node) 

139 self.set_local(name or child_node.name, child_node) # type: ignore[attr-defined] 

140 

141 def __getitem__(self, item: str) -> SuccessfulInferenceResult: 

142 """The first node the defines the given local. 

143 

144 :param item: The name of the locally defined object. 

145 

146 :raises KeyError: If the name is not defined. 

147 """ 

148 return self.locals[item][0] 

149 

150 def __iter__(self): 

151 """Iterate over the names of locals defined in this scoped node. 

152 

153 :returns: The names of the defined locals. 

154 :rtype: iterable(str) 

155 """ 

156 return iter(self.keys()) 

157 

158 def keys(self): 

159 """The names of locals defined in this scoped node. 

160 

161 :returns: The names of the defined locals. 

162 :rtype: list(str) 

163 """ 

164 return list(self.locals.keys()) 

165 

166 def values(self): 

167 """The nodes that define the locals in this scoped node. 

168 

169 :returns: The nodes that define locals. 

170 :rtype: list(NodeNG) 

171 """ 

172 # pylint: disable=consider-using-dict-items 

173 # It look like this class override items/keys/values, 

174 # probably not worth the headache 

175 return [self[key] for key in self.keys()] 

176 

177 def items(self): 

178 """Get the names of the locals and the node that defines the local. 

179 

180 :returns: The names of locals and their associated node. 

181 :rtype: list(tuple(str, NodeNG)) 

182 """ 

183 return list(zip(self.keys(), self.values())) 

184 

185 def __contains__(self, name) -> bool: 

186 """Check if a local is defined in this scope. 

187 

188 :param name: The name of the local to check for. 

189 :type name: str 

190 

191 :returns: Whether this node has a local of the given name, 

192 """ 

193 return name in self.locals 

194 

195 

196class ComprehensionScope(LocalsDictNodeNG): 

197 """Scoping for different types of comprehensions.""" 

198 

199 scope_lookup = LocalsDictNodeNG._scope_lookup 

200 

201 generators: list[nodes.Comprehension] 

202 """The generators that are looped through."""