Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/filters.py: 46%

220 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1""" 

2Filters are objects that you can use to filter names in different scopes. They 

3are needed for name resolution. 

4""" 

5from abc import abstractmethod 

6from typing import List, MutableMapping, Type 

7import weakref 

8 

9from parso.tree import search_ancestor 

10from parso.python.tree import Name, UsedNamesMapping 

11 

12from jedi.inference import flow_analysis 

13from jedi.inference.base_value import ValueSet, ValueWrapper, \ 

14 LazyValueWrapper 

15from jedi.parser_utils import get_cached_parent_scope, get_parso_cache_node 

16from jedi.inference.utils import to_list 

17from jedi.inference.names import TreeNameDefinition, ParamName, \ 

18 AnonymousParamName, AbstractNameDefinition, NameWrapper 

19 

20_definition_name_cache: MutableMapping[UsedNamesMapping, List[Name]] 

21_definition_name_cache = weakref.WeakKeyDictionary() 

22 

23 

24class AbstractFilter: 

25 _until_position = None 

26 

27 def _filter(self, names): 

28 if self._until_position is not None: 

29 return [n for n in names if n.start_pos < self._until_position] 

30 return names 

31 

32 @abstractmethod 

33 def get(self, name): 

34 raise NotImplementedError 

35 

36 @abstractmethod 

37 def values(self): 

38 raise NotImplementedError 

39 

40 

41class FilterWrapper: 

42 name_wrapper_class: Type[NameWrapper] 

43 

44 def __init__(self, wrapped_filter): 

45 self._wrapped_filter = wrapped_filter 

46 

47 def wrap_names(self, names): 

48 return [self.name_wrapper_class(name) for name in names] 

49 

50 def get(self, name): 

51 return self.wrap_names(self._wrapped_filter.get(name)) 

52 

53 def values(self): 

54 return self.wrap_names(self._wrapped_filter.values()) 

55 

56 

57def _get_definition_names(parso_cache_node, used_names, name_key): 

58 if parso_cache_node is None: 

59 names = used_names.get(name_key, ()) 

60 return tuple(name for name in names if name.is_definition(include_setitem=True)) 

61 

62 try: 

63 for_module = _definition_name_cache[parso_cache_node] 

64 except KeyError: 

65 for_module = _definition_name_cache[parso_cache_node] = {} 

66 

67 try: 

68 return for_module[name_key] 

69 except KeyError: 

70 names = used_names.get(name_key, ()) 

71 result = for_module[name_key] = tuple( 

72 name for name in names if name.is_definition(include_setitem=True) 

73 ) 

74 return result 

75 

76 

77class _AbstractUsedNamesFilter(AbstractFilter): 

78 name_class = TreeNameDefinition 

79 

80 def __init__(self, parent_context, node_context=None): 

81 if node_context is None: 

82 node_context = parent_context 

83 self._node_context = node_context 

84 self._parser_scope = node_context.tree_node 

85 module_context = node_context.get_root_context() 

86 # It is quite hacky that we have to use that. This is for caching 

87 # certain things with a WeakKeyDictionary. However, parso intentionally 

88 # uses slots (to save memory) and therefore we end up with having to 

89 # have a weak reference to the object that caches the tree. 

90 # 

91 # Previously we have tried to solve this by using a weak reference onto 

92 # used_names. However that also does not work, because it has a 

93 # reference from the module, which itself is referenced by any node 

94 # through parents. 

95 path = module_context.py__file__() 

96 if path is None: 

97 # If the path is None, there is no guarantee that parso caches it. 

98 self._parso_cache_node = None 

99 else: 

100 self._parso_cache_node = get_parso_cache_node( 

101 module_context.inference_state.latest_grammar 

102 if module_context.is_stub() else module_context.inference_state.grammar, 

103 path 

104 ) 

105 self._used_names = module_context.tree_node.get_used_names() 

106 self.parent_context = parent_context 

107 

108 def get(self, name): 

109 return self._convert_names(self._filter( 

110 _get_definition_names(self._parso_cache_node, self._used_names, name), 

111 )) 

112 

113 def _convert_names(self, names): 

114 return [self.name_class(self.parent_context, name) for name in names] 

115 

116 def values(self): 

117 return self._convert_names( 

118 name 

119 for name_key in self._used_names 

120 for name in self._filter( 

121 _get_definition_names(self._parso_cache_node, self._used_names, name_key), 

122 ) 

123 ) 

124 

125 def __repr__(self): 

126 return '<%s: %s>' % (self.__class__.__name__, self.parent_context) 

127 

128 

129class ParserTreeFilter(_AbstractUsedNamesFilter): 

130 def __init__(self, parent_context, node_context=None, until_position=None, 

131 origin_scope=None): 

132 """ 

133 node_context is an option to specify a second value for use cases 

134 like the class mro where the parent class of a new name would be the 

135 value, but for some type inference it's important to have a local 

136 value of the other classes. 

137 """ 

138 super().__init__(parent_context, node_context) 

139 self._origin_scope = origin_scope 

140 self._until_position = until_position 

141 

142 def _filter(self, names): 

143 names = super()._filter(names) 

144 names = [n for n in names if self._is_name_reachable(n)] 

145 return list(self._check_flows(names)) 

146 

147 def _is_name_reachable(self, name): 

148 parent = name.parent 

149 if parent.type == 'trailer': 

150 return False 

151 base_node = parent if parent.type in ('classdef', 'funcdef') else name 

152 return get_cached_parent_scope(self._parso_cache_node, base_node) == self._parser_scope 

153 

154 def _check_flows(self, names): 

155 for name in sorted(names, key=lambda name: name.start_pos, reverse=True): 

156 check = flow_analysis.reachability_check( 

157 context=self._node_context, 

158 value_scope=self._parser_scope, 

159 node=name, 

160 origin_scope=self._origin_scope 

161 ) 

162 if check is not flow_analysis.UNREACHABLE: 

163 yield name 

164 

165 if check is flow_analysis.REACHABLE: 

166 break 

167 

168 

169class _FunctionExecutionFilter(ParserTreeFilter): 

170 def __init__(self, parent_context, function_value, until_position, origin_scope): 

171 super().__init__( 

172 parent_context, 

173 until_position=until_position, 

174 origin_scope=origin_scope, 

175 ) 

176 self._function_value = function_value 

177 

178 def _convert_param(self, param, name): 

179 raise NotImplementedError 

180 

181 @to_list 

182 def _convert_names(self, names): 

183 for name in names: 

184 param = search_ancestor(name, 'param') 

185 # Here we don't need to check if the param is a default/annotation, 

186 # because those are not definitions and never make it to this 

187 # point. 

188 if param: 

189 yield self._convert_param(param, name) 

190 else: 

191 yield TreeNameDefinition(self.parent_context, name) 

192 

193 

194class FunctionExecutionFilter(_FunctionExecutionFilter): 

195 def __init__(self, *args, arguments, **kwargs): 

196 super().__init__(*args, **kwargs) 

197 self._arguments = arguments 

198 

199 def _convert_param(self, param, name): 

200 return ParamName(self._function_value, name, self._arguments) 

201 

202 

203class AnonymousFunctionExecutionFilter(_FunctionExecutionFilter): 

204 def _convert_param(self, param, name): 

205 return AnonymousParamName(self._function_value, name) 

206 

207 

208class GlobalNameFilter(_AbstractUsedNamesFilter): 

209 def get(self, name): 

210 try: 

211 names = self._used_names[name] 

212 except KeyError: 

213 return [] 

214 return self._convert_names(self._filter(names)) 

215 

216 @to_list 

217 def _filter(self, names): 

218 for name in names: 

219 if name.parent.type == 'global_stmt': 

220 yield name 

221 

222 def values(self): 

223 return self._convert_names( 

224 name for name_list in self._used_names.values() 

225 for name in self._filter(name_list) 

226 ) 

227 

228 

229class DictFilter(AbstractFilter): 

230 def __init__(self, dct): 

231 self._dct = dct 

232 

233 def get(self, name): 

234 try: 

235 value = self._convert(name, self._dct[name]) 

236 except KeyError: 

237 return [] 

238 else: 

239 return list(self._filter([value])) 

240 

241 def values(self): 

242 def yielder(): 

243 for item in self._dct.items(): 

244 try: 

245 yield self._convert(*item) 

246 except KeyError: 

247 pass 

248 return self._filter(yielder()) 

249 

250 def _convert(self, name, value): 

251 return value 

252 

253 def __repr__(self): 

254 keys = ', '.join(self._dct.keys()) 

255 return '<%s: for {%s}>' % (self.__class__.__name__, keys) 

256 

257 

258class MergedFilter: 

259 def __init__(self, *filters): 

260 self._filters = filters 

261 

262 def get(self, name): 

263 return [n for filter in self._filters for n in filter.get(name)] 

264 

265 def values(self): 

266 return [n for filter in self._filters for n in filter.values()] 

267 

268 def __repr__(self): 

269 return '%s(%s)' % (self.__class__.__name__, ', '.join(str(f) for f in self._filters)) 

270 

271 

272class _BuiltinMappedMethod(ValueWrapper): 

273 """``Generator.__next__`` ``dict.values`` methods and so on.""" 

274 api_type = 'function' 

275 

276 def __init__(self, value, method, builtin_func): 

277 super().__init__(builtin_func) 

278 self._value = value 

279 self._method = method 

280 

281 def py__call__(self, arguments): 

282 # TODO add TypeError if params are given/or not correct. 

283 return self._method(self._value, arguments) 

284 

285 

286class SpecialMethodFilter(DictFilter): 

287 """ 

288 A filter for methods that are defined in this module on the corresponding 

289 classes like Generator (for __next__, etc). 

290 """ 

291 class SpecialMethodName(AbstractNameDefinition): 

292 api_type = 'function' 

293 

294 def __init__(self, parent_context, string_name, callable_, builtin_value): 

295 self.parent_context = parent_context 

296 self.string_name = string_name 

297 self._callable = callable_ 

298 self._builtin_value = builtin_value 

299 

300 def infer(self): 

301 for filter in self._builtin_value.get_filters(): 

302 # We can take the first index, because on builtin methods there's 

303 # always only going to be one name. The same is true for the 

304 # inferred values. 

305 for name in filter.get(self.string_name): 

306 builtin_func = next(iter(name.infer())) 

307 break 

308 else: 

309 continue 

310 break 

311 return ValueSet([ 

312 _BuiltinMappedMethod(self.parent_context, self._callable, builtin_func) 

313 ]) 

314 

315 def __init__(self, value, dct, builtin_value): 

316 super().__init__(dct) 

317 self.value = value 

318 self._builtin_value = builtin_value 

319 """ 

320 This value is what will be used to introspect the name, where as the 

321 other value will be used to execute the function. 

322 

323 We distinguish, because we have to. 

324 """ 

325 

326 def _convert(self, name, value): 

327 return self.SpecialMethodName(self.value, name, value, self._builtin_value) 

328 

329 

330class _OverwriteMeta(type): 

331 def __init__(cls, name, bases, dct): 

332 super().__init__(name, bases, dct) 

333 

334 base_dct = {} 

335 for base_cls in reversed(cls.__bases__): 

336 try: 

337 base_dct.update(base_cls.overwritten_methods) 

338 except AttributeError: 

339 pass 

340 

341 for func in cls.__dict__.values(): 

342 try: 

343 base_dct.update(func.registered_overwritten_methods) 

344 except AttributeError: 

345 pass 

346 cls.overwritten_methods = base_dct 

347 

348 

349class _AttributeOverwriteMixin: 

350 def get_filters(self, *args, **kwargs): 

351 yield SpecialMethodFilter(self, self.overwritten_methods, self._wrapped_value) 

352 yield from self._wrapped_value.get_filters(*args, **kwargs) 

353 

354 

355class LazyAttributeOverwrite(_AttributeOverwriteMixin, LazyValueWrapper, 

356 metaclass=_OverwriteMeta): 

357 def __init__(self, inference_state): 

358 self.inference_state = inference_state 

359 

360 

361class AttributeOverwrite(_AttributeOverwriteMixin, ValueWrapper, 

362 metaclass=_OverwriteMeta): 

363 pass 

364 

365 

366def publish_method(method_name): 

367 def decorator(func): 

368 dct = func.__dict__.setdefault('registered_overwritten_methods', {}) 

369 dct[method_name] = func 

370 return func 

371 return decorator