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
« 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
9from parso.tree import search_ancestor
10from parso.python.tree import Name, UsedNamesMapping
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
20_definition_name_cache: MutableMapping[UsedNamesMapping, List[Name]]
21_definition_name_cache = weakref.WeakKeyDictionary()
24class AbstractFilter:
25 _until_position = None
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
32 @abstractmethod
33 def get(self, name):
34 raise NotImplementedError
36 @abstractmethod
37 def values(self):
38 raise NotImplementedError
41class FilterWrapper:
42 name_wrapper_class: Type[NameWrapper]
44 def __init__(self, wrapped_filter):
45 self._wrapped_filter = wrapped_filter
47 def wrap_names(self, names):
48 return [self.name_wrapper_class(name) for name in names]
50 def get(self, name):
51 return self.wrap_names(self._wrapped_filter.get(name))
53 def values(self):
54 return self.wrap_names(self._wrapped_filter.values())
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))
62 try:
63 for_module = _definition_name_cache[parso_cache_node]
64 except KeyError:
65 for_module = _definition_name_cache[parso_cache_node] = {}
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
77class _AbstractUsedNamesFilter(AbstractFilter):
78 name_class = TreeNameDefinition
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
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 ))
113 def _convert_names(self, names):
114 return [self.name_class(self.parent_context, name) for name in names]
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 )
125 def __repr__(self):
126 return '<%s: %s>' % (self.__class__.__name__, self.parent_context)
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
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))
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
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
165 if check is flow_analysis.REACHABLE:
166 break
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
178 def _convert_param(self, param, name):
179 raise NotImplementedError
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)
194class FunctionExecutionFilter(_FunctionExecutionFilter):
195 def __init__(self, *args, arguments, **kwargs):
196 super().__init__(*args, **kwargs)
197 self._arguments = arguments
199 def _convert_param(self, param, name):
200 return ParamName(self._function_value, name, self._arguments)
203class AnonymousFunctionExecutionFilter(_FunctionExecutionFilter):
204 def _convert_param(self, param, name):
205 return AnonymousParamName(self._function_value, name)
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))
216 @to_list
217 def _filter(self, names):
218 for name in names:
219 if name.parent.type == 'global_stmt':
220 yield name
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 )
229class DictFilter(AbstractFilter):
230 def __init__(self, dct):
231 self._dct = dct
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]))
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())
250 def _convert(self, name, value):
251 return value
253 def __repr__(self):
254 keys = ', '.join(self._dct.keys())
255 return '<%s: for {%s}>' % (self.__class__.__name__, keys)
258class MergedFilter:
259 def __init__(self, *filters):
260 self._filters = filters
262 def get(self, name):
263 return [n for filter in self._filters for n in filter.get(name)]
265 def values(self):
266 return [n for filter in self._filters for n in filter.values()]
268 def __repr__(self):
269 return '%s(%s)' % (self.__class__.__name__, ', '.join(str(f) for f in self._filters))
272class _BuiltinMappedMethod(ValueWrapper):
273 """``Generator.__next__`` ``dict.values`` methods and so on."""
274 api_type = 'function'
276 def __init__(self, value, method, builtin_func):
277 super().__init__(builtin_func)
278 self._value = value
279 self._method = method
281 def py__call__(self, arguments):
282 # TODO add TypeError if params are given/or not correct.
283 return self._method(self._value, arguments)
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'
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
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 ])
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.
323 We distinguish, because we have to.
324 """
326 def _convert(self, name, value):
327 return self.SpecialMethodName(self.value, name, value, self._builtin_value)
330class _OverwriteMeta(type):
331 def __init__(cls, name, bases, dct):
332 super().__init__(name, bases, dct)
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
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
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)
355class LazyAttributeOverwrite(_AttributeOverwriteMixin, LazyValueWrapper,
356 metaclass=_OverwriteMeta):
357 def __init__(self, inference_state):
358 self.inference_state = inference_state
361class AttributeOverwrite(_AttributeOverwriteMixin, ValueWrapper,
362 metaclass=_OverwriteMeta):
363 pass
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