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

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

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING, TypeVar, overload 

10 

11from astroid.filter_statements import _filter_stmts 

12from astroid.nodes import node_classes, scoped_nodes 

13from astroid.nodes.scoped_nodes.utils import builtin_lookup 

14from astroid.typing import InferenceResult, SuccessfulInferenceResult 

15 

16if TYPE_CHECKING: 

17 from astroid import nodes 

18 

19_T = TypeVar("_T") 

20 

21 

22class LocalsDictNodeNG(node_classes.LookupMixIn): 

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

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

25 to locals information 

26 """ 

27 

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

29 

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 or isinstance(self.parent, node_classes.Unknown): 

43 return self.name 

44 return f"{self.parent.frame(future=True).qname()}.{self.name}" 

45 

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

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

48 

49 :returns: The first parent scope node. 

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

51 """ 

52 return self 

53 

54 def scope_lookup( 

55 self, node: node_classes.LookupMixIn, name: str, offset: int = 0 

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

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

58 

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

60 Any assignments after the given node are ignored. 

61 

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

63 

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

65 

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

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

68 globals or builtin). 

69 """ 

70 raise NotImplementedError 

71 

72 def _scope_lookup( 

73 self, node: node_classes.LookupMixIn, name: str, offset: int = 0 

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

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

76 try: 

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

78 except KeyError: 

79 stmts = () 

80 if stmts: 

81 return self, stmts 

82 

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

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

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

86 while pscope is not None: 

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

88 return pscope.scope_lookup(node, name) 

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

90 

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

92 return builtin_lookup(name) 

93 

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

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

96 

97 .. seealso:: :meth:`scope` 

98 

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

100 

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

102 """ 

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

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

105 

106 __setitem__ = set_local 

107 

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

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

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

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

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

113 # to spend development time on it. 

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

115 child.parent = self 

116 

117 @overload 

118 def add_local_node( 

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

120 ) -> None: 

121 ... 

122 

123 @overload 

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

125 ... 

126 

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

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

129 

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

131 

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

133 the given child node. 

134 """ 

135 if name != "__class__": 

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

137 self._append_node(child_node) 

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

139 

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

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

142 

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

144 

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

146 """ 

147 return self.locals[item][0] 

148 

149 def __iter__(self): 

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

151 

152 :returns: The names of the defined locals. 

153 :rtype: iterable(str) 

154 """ 

155 return iter(self.keys()) 

156 

157 def keys(self): 

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

159 

160 :returns: The names of the defined locals. 

161 :rtype: list(str) 

162 """ 

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

164 

165 def values(self): 

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

167 

168 :returns: The nodes that define locals. 

169 :rtype: list(NodeNG) 

170 """ 

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

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

173 # probably not worth the headache 

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

175 

176 def items(self): 

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

178 

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

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

181 """ 

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

183 

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

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

186 

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

188 :type name: str 

189 

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

191 """ 

192 return name in self.locals 

193 

194 

195class ComprehensionScope(LocalsDictNodeNG): 

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

197 

198 scope_lookup = LocalsDictNodeNG._scope_lookup 

199 

200 generators: list[nodes.Comprehension] 

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