Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/compiled/access.py: 25%
326 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
1import inspect
2import types
3import traceback
4import sys
5import operator as op
6from collections import namedtuple
7import warnings
8import re
9import builtins
10import typing
11from pathlib import Path
12from typing import Optional
14from jedi.inference.compiled.getattr_static import getattr_static
16ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict)
18MethodDescriptorType = type(str.replace)
19# These are not considered classes and access is granted even though they have
20# a __class__ attribute.
21NOT_CLASS_TYPES = (
22 types.BuiltinFunctionType,
23 types.CodeType,
24 types.FrameType,
25 types.FunctionType,
26 types.GeneratorType,
27 types.GetSetDescriptorType,
28 types.LambdaType,
29 types.MemberDescriptorType,
30 types.MethodType,
31 types.ModuleType,
32 types.TracebackType,
33 MethodDescriptorType,
34 types.MappingProxyType,
35 types.SimpleNamespace,
36 types.DynamicClassAttribute,
37)
39# Those types don't exist in typing.
40MethodDescriptorType = type(str.replace)
41WrapperDescriptorType = type(set.__iter__)
42# `object.__subclasshook__` is an already executed descriptor.
43object_class_dict = type.__dict__["__dict__"].__get__(object)
44ClassMethodDescriptorType = type(object_class_dict['__subclasshook__'])
46_sentinel = object()
48# Maps Python syntax to the operator module.
49COMPARISON_OPERATORS = {
50 '==': op.eq,
51 '!=': op.ne,
52 'is': op.is_,
53 'is not': op.is_not,
54 '<': op.lt,
55 '<=': op.le,
56 '>': op.gt,
57 '>=': op.ge,
58}
60_OPERATORS = {
61 '+': op.add,
62 '-': op.sub,
63}
64_OPERATORS.update(COMPARISON_OPERATORS)
66ALLOWED_DESCRIPTOR_ACCESS = (
67 types.FunctionType,
68 types.GetSetDescriptorType,
69 types.MemberDescriptorType,
70 MethodDescriptorType,
71 WrapperDescriptorType,
72 ClassMethodDescriptorType,
73 staticmethod,
74 classmethod,
75)
78def safe_getattr(obj, name, default=_sentinel):
79 try:
80 attr, is_get_descriptor = getattr_static(obj, name)
81 except AttributeError:
82 if default is _sentinel:
83 raise
84 return default
85 else:
86 if isinstance(attr, ALLOWED_DESCRIPTOR_ACCESS):
87 # In case of descriptors that have get methods we cannot return
88 # it's value, because that would mean code execution.
89 # Since it's an isinstance call, code execution is still possible,
90 # but this is not really a security feature, but much more of a
91 # safety feature. Code execution is basically always possible when
92 # a module is imported. This is here so people don't shoot
93 # themselves in the foot.
94 return getattr(obj, name)
95 return attr
98SignatureParam = namedtuple(
99 'SignatureParam',
100 'name has_default default default_string has_annotation annotation annotation_string kind_name'
101)
104def shorten_repr(func):
105 def wrapper(self):
106 r = func(self)
107 if len(r) > 50:
108 r = r[:50] + '..'
109 return r
110 return wrapper
113def create_access(inference_state, obj):
114 return inference_state.compiled_subprocess.get_or_create_access_handle(obj)
117def load_module(inference_state, dotted_name, sys_path):
118 temp, sys.path = sys.path, sys_path
119 try:
120 __import__(dotted_name)
121 except ImportError:
122 # If a module is "corrupt" or not really a Python module or whatever.
123 warnings.warn(
124 "Module %s not importable in path %s." % (dotted_name, sys_path),
125 UserWarning,
126 stacklevel=2,
127 )
128 return None
129 except Exception:
130 # Since __import__ pretty much makes code execution possible, just
131 # catch any error here and print it.
132 warnings.warn(
133 "Cannot import:\n%s" % traceback.format_exc(), UserWarning, stacklevel=2
134 )
135 return None
136 finally:
137 sys.path = temp
139 # Just access the cache after import, because of #59 as well as the very
140 # complicated import structure of Python.
141 module = sys.modules[dotted_name]
142 return create_access_path(inference_state, module)
145class AccessPath:
146 def __init__(self, accesses):
147 self.accesses = accesses
150def create_access_path(inference_state, obj):
151 access = create_access(inference_state, obj)
152 return AccessPath(access.get_access_path_tuples())
155def get_api_type(obj):
156 if inspect.isclass(obj):
157 return 'class'
158 elif inspect.ismodule(obj):
159 return 'module'
160 elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
161 or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
162 return 'function'
163 # Everything else...
164 return 'instance'
167class DirectObjectAccess:
168 def __init__(self, inference_state, obj):
169 self._inference_state = inference_state
170 self._obj = obj
172 def __repr__(self):
173 return '%s(%s)' % (self.__class__.__name__, self.get_repr())
175 def _create_access(self, obj):
176 return create_access(self._inference_state, obj)
178 def _create_access_path(self, obj):
179 return create_access_path(self._inference_state, obj)
181 def py__bool__(self):
182 return bool(self._obj)
184 def py__file__(self) -> Optional[Path]:
185 try:
186 return Path(self._obj.__file__)
187 except AttributeError:
188 return None
190 def py__doc__(self):
191 return inspect.getdoc(self._obj) or ''
193 def py__name__(self):
194 if not _is_class_instance(self._obj) or \
195 inspect.ismethoddescriptor(self._obj): # slots
196 cls = self._obj
197 else:
198 try:
199 cls = self._obj.__class__
200 except AttributeError:
201 # happens with numpy.core.umath._UFUNC_API (you get it
202 # automatically by doing `import numpy`.
203 return None
205 try:
206 return cls.__name__
207 except AttributeError:
208 return None
210 def py__mro__accesses(self):
211 return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:])
213 def py__getitem__all_values(self):
214 if isinstance(self._obj, dict):
215 return [self._create_access_path(v) for v in self._obj.values()]
216 if isinstance(self._obj, (list, tuple)):
217 return [self._create_access_path(v) for v in self._obj]
219 if self.is_instance():
220 cls = DirectObjectAccess(self._inference_state, self._obj.__class__)
221 return cls.py__getitem__all_values()
223 try:
224 getitem = self._obj.__getitem__
225 except AttributeError:
226 pass
227 else:
228 annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation()
229 if annotation is not None:
230 return [annotation]
231 return None
233 def py__simple_getitem__(self, index):
234 if type(self._obj) not in ALLOWED_GETITEM_TYPES:
235 # Get rid of side effects, we won't call custom `__getitem__`s.
236 return None
238 return self._create_access_path(self._obj[index])
240 def py__iter__list(self):
241 try:
242 iter_method = self._obj.__iter__
243 except AttributeError:
244 return None
245 else:
246 p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation()
247 if p is not None:
248 return [p]
250 if type(self._obj) not in ALLOWED_GETITEM_TYPES:
251 # Get rid of side effects, we won't call custom `__getitem__`s.
252 return []
254 lst = []
255 for i, part in enumerate(self._obj):
256 if i > 20:
257 # Should not go crazy with large iterators
258 break
259 lst.append(self._create_access_path(part))
260 return lst
262 def py__class__(self):
263 return self._create_access_path(self._obj.__class__)
265 def py__bases__(self):
266 return [self._create_access_path(base) for base in self._obj.__bases__]
268 def py__path__(self):
269 paths = getattr(self._obj, '__path__', None)
270 # Avoid some weird hacks that would just fail, because they cannot be
271 # used by pickle.
272 if not isinstance(paths, list) \
273 or not all(isinstance(p, str) for p in paths):
274 return None
275 return paths
277 @shorten_repr
278 def get_repr(self):
279 if inspect.ismodule(self._obj):
280 return repr(self._obj)
281 # Try to avoid execution of the property.
282 if safe_getattr(self._obj, '__module__', default='') == 'builtins':
283 return repr(self._obj)
285 type_ = type(self._obj)
286 if type_ == type:
287 return type.__repr__(self._obj)
289 if safe_getattr(type_, '__module__', default='') == 'builtins':
290 # Allow direct execution of repr for builtins.
291 return repr(self._obj)
292 return object.__repr__(self._obj)
294 def is_class(self):
295 return inspect.isclass(self._obj)
297 def is_function(self):
298 return inspect.isfunction(self._obj) or inspect.ismethod(self._obj)
300 def is_module(self):
301 return inspect.ismodule(self._obj)
303 def is_instance(self):
304 return _is_class_instance(self._obj)
306 def ismethoddescriptor(self):
307 return inspect.ismethoddescriptor(self._obj)
309 def get_qualified_names(self):
310 def try_to_get_name(obj):
311 return getattr(obj, '__qualname__', getattr(obj, '__name__', None))
313 if self.is_module():
314 return ()
315 name = try_to_get_name(self._obj)
316 if name is None:
317 name = try_to_get_name(type(self._obj))
318 if name is None:
319 return ()
320 return tuple(name.split('.'))
322 def dir(self):
323 return dir(self._obj)
325 def has_iter(self):
326 try:
327 iter(self._obj)
328 return True
329 except TypeError:
330 return False
332 def is_allowed_getattr(self, name, safe=True):
333 # TODO this API is ugly.
334 if not safe:
335 # Unsafe is mostly used to check for __getattr__/__getattribute__.
336 # getattr_static works for properties, but the underscore methods
337 # are just ignored (because it's safer and avoids more code
338 # execution). See also GH #1378.
340 # Avoid warnings, see comment in the next function.
341 with warnings.catch_warnings(record=True):
342 warnings.simplefilter("always")
343 try:
344 return hasattr(self._obj, name), False
345 except Exception:
346 # Obviously has an attribute (propably a property) that
347 # gets executed, so just avoid all exceptions here.
348 return False, False
349 try:
350 attr, is_get_descriptor = getattr_static(self._obj, name)
351 except AttributeError:
352 return False, False
353 else:
354 if is_get_descriptor and type(attr) not in ALLOWED_DESCRIPTOR_ACCESS:
355 # In case of descriptors that have get methods we cannot return
356 # it's value, because that would mean code execution.
357 return True, True
358 return True, False
360 def getattr_paths(self, name, default=_sentinel):
361 try:
362 # Make sure no warnings are printed here, this is autocompletion,
363 # warnings should not be shown. See also GH #1383.
364 with warnings.catch_warnings(record=True):
365 warnings.simplefilter("always")
366 return_obj = getattr(self._obj, name)
367 except Exception as e:
368 if default is _sentinel:
369 if isinstance(e, AttributeError):
370 # Happens e.g. in properties of
371 # PyQt4.QtGui.QStyleOptionComboBox.currentText
372 # -> just set it to None
373 raise
374 # Just in case anything happens, return an AttributeError. It
375 # should not crash.
376 raise AttributeError
377 return_obj = default
378 access = self._create_access(return_obj)
379 if inspect.ismodule(return_obj):
380 return [access]
382 try:
383 module = return_obj.__module__
384 except AttributeError:
385 pass
386 else:
387 if module is not None and isinstance(module, str):
388 try:
389 __import__(module)
390 # For some modules like _sqlite3, the __module__ for classes is
391 # different, in this case it's sqlite3. So we have to try to
392 # load that "original" module, because it's not loaded yet. If
393 # we don't do that, we don't really have a "parent" module and
394 # we would fall back to builtins.
395 except ImportError:
396 pass
398 module = inspect.getmodule(return_obj)
399 if module is None:
400 module = inspect.getmodule(type(return_obj))
401 if module is None:
402 module = builtins
403 return [self._create_access(module), access]
405 def get_safe_value(self):
406 if type(self._obj) in (bool, bytes, float, int, str, slice) or self._obj is None:
407 return self._obj
408 raise ValueError("Object is type %s and not simple" % type(self._obj))
410 def get_api_type(self):
411 return get_api_type(self._obj)
413 def get_array_type(self):
414 if isinstance(self._obj, dict):
415 return 'dict'
416 return None
418 def get_key_paths(self):
419 def iter_partial_keys():
420 # We could use list(keys()), but that might take a lot more memory.
421 for (i, k) in enumerate(self._obj.keys()):
422 # Limit key listing at some point. This is artificial, but this
423 # way we don't get stalled because of slow completions
424 if i > 50:
425 break
426 yield k
428 return [self._create_access_path(k) for k in iter_partial_keys()]
430 def get_access_path_tuples(self):
431 accesses = [create_access(self._inference_state, o) for o in self._get_objects_path()]
432 return [(access.py__name__(), access) for access in accesses]
434 def _get_objects_path(self):
435 def get():
436 obj = self._obj
437 yield obj
438 try:
439 obj = obj.__objclass__
440 except AttributeError:
441 pass
442 else:
443 yield obj
445 try:
446 # Returns a dotted string path.
447 imp_plz = obj.__module__
448 except AttributeError:
449 # Unfortunately in some cases like `int` there's no __module__
450 if not inspect.ismodule(obj):
451 yield builtins
452 else:
453 if imp_plz is None:
454 # Happens for example in `(_ for _ in []).send.__module__`.
455 yield builtins
456 else:
457 try:
458 yield sys.modules[imp_plz]
459 except KeyError:
460 # __module__ can be something arbitrary that doesn't exist.
461 yield builtins
463 return list(reversed(list(get())))
465 def execute_operation(self, other_access_handle, operator):
466 other_access = other_access_handle.access
467 op = _OPERATORS[operator]
468 return self._create_access_path(op(self._obj, other_access._obj))
470 def get_annotation_name_and_args(self):
471 """
472 Returns Tuple[Optional[str], Tuple[AccessPath, ...]]
473 """
474 name = None
475 args = ()
476 if safe_getattr(self._obj, '__module__', default='') == 'typing':
477 m = re.match(r'typing.(\w+)\[', repr(self._obj))
478 if m is not None:
479 name = m.group(1)
481 import typing
482 if sys.version_info >= (3, 8):
483 args = typing.get_args(self._obj)
484 else:
485 args = safe_getattr(self._obj, '__args__', default=None)
486 return name, tuple(self._create_access_path(arg) for arg in args)
488 def needs_type_completions(self):
489 return inspect.isclass(self._obj) and self._obj != type
491 def _annotation_to_str(self, annotation):
492 return inspect.formatannotation(annotation)
494 def get_signature_params(self):
495 return [
496 SignatureParam(
497 name=p.name,
498 has_default=p.default is not p.empty,
499 default=self._create_access_path(p.default),
500 default_string=repr(p.default),
501 has_annotation=p.annotation is not p.empty,
502 annotation=self._create_access_path(p.annotation),
503 annotation_string=self._annotation_to_str(p.annotation),
504 kind_name=str(p.kind)
505 ) for p in self._get_signature().parameters.values()
506 ]
508 def _get_signature(self):
509 obj = self._obj
510 try:
511 return inspect.signature(obj)
512 except (RuntimeError, TypeError):
513 # Reading the code of the function in Python 3.6 implies there are
514 # at least these errors that might occur if something is wrong with
515 # the signature. In that case we just want a simple escape for now.
516 raise ValueError
518 def get_return_annotation(self):
519 try:
520 o = self._obj.__annotations__.get('return')
521 except AttributeError:
522 return None
524 if o is None:
525 return None
527 try:
528 o = typing.get_type_hints(self._obj).get('return')
529 except Exception:
530 pass
532 return self._create_access_path(o)
534 def negate(self):
535 return self._create_access_path(-self._obj)
537 def get_dir_infos(self):
538 """
539 Used to return a couple of infos that are needed when accessing the sub
540 objects of an objects
541 """
542 tuples = dict(
543 (name, self.is_allowed_getattr(name))
544 for name in self.dir()
545 )
546 return self.needs_type_completions(), tuples
549def _is_class_instance(obj):
550 """Like inspect.* methods."""
551 try:
552 cls = obj.__class__
553 except AttributeError:
554 return False
555 else:
556 # The isinstance check for cls is just there so issubclass doesn't
557 # raise an exception.
558 return cls != type and isinstance(cls, type) and not issubclass(cls, NOT_CLASS_TYPES)