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