You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			563 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			563 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
import inspect
 | 
						|
import types
 | 
						|
import traceback
 | 
						|
import sys
 | 
						|
import operator as op
 | 
						|
from collections import namedtuple
 | 
						|
import warnings
 | 
						|
import re
 | 
						|
import builtins
 | 
						|
import typing
 | 
						|
from pathlib import Path
 | 
						|
from typing import Optional, Tuple
 | 
						|
 | 
						|
from jedi.inference.compiled.getattr_static import getattr_static
 | 
						|
 | 
						|
ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict)
 | 
						|
 | 
						|
MethodDescriptorType = type(str.replace)
 | 
						|
# These are not considered classes and access is granted even though they have
 | 
						|
# a __class__ attribute.
 | 
						|
NOT_CLASS_TYPES = (
 | 
						|
    types.BuiltinFunctionType,
 | 
						|
    types.CodeType,
 | 
						|
    types.FrameType,
 | 
						|
    types.FunctionType,
 | 
						|
    types.GeneratorType,
 | 
						|
    types.GetSetDescriptorType,
 | 
						|
    types.LambdaType,
 | 
						|
    types.MemberDescriptorType,
 | 
						|
    types.MethodType,
 | 
						|
    types.ModuleType,
 | 
						|
    types.TracebackType,
 | 
						|
    MethodDescriptorType,
 | 
						|
    types.MappingProxyType,
 | 
						|
    types.SimpleNamespace,
 | 
						|
    types.DynamicClassAttribute,
 | 
						|
)
 | 
						|
 | 
						|
# Those types don't exist in typing.
 | 
						|
MethodDescriptorType = type(str.replace)
 | 
						|
WrapperDescriptorType = type(set.__iter__)
 | 
						|
# `object.__subclasshook__` is an already executed descriptor.
 | 
						|
object_class_dict = type.__dict__["__dict__"].__get__(object)  # type: ignore[index]
 | 
						|
ClassMethodDescriptorType = type(object_class_dict['__subclasshook__'])
 | 
						|
 | 
						|
_sentinel = object()
 | 
						|
 | 
						|
# Maps Python syntax to the operator module.
 | 
						|
COMPARISON_OPERATORS = {
 | 
						|
    '==': op.eq,
 | 
						|
    '!=': op.ne,
 | 
						|
    'is': op.is_,
 | 
						|
    'is not': op.is_not,
 | 
						|
    '<': op.lt,
 | 
						|
    '<=': op.le,
 | 
						|
    '>': op.gt,
 | 
						|
    '>=': op.ge,
 | 
						|
}
 | 
						|
 | 
						|
_OPERATORS = {
 | 
						|
    '+': op.add,
 | 
						|
    '-': op.sub,
 | 
						|
}
 | 
						|
_OPERATORS.update(COMPARISON_OPERATORS)
 | 
						|
 | 
						|
ALLOWED_DESCRIPTOR_ACCESS = (
 | 
						|
    types.FunctionType,
 | 
						|
    types.GetSetDescriptorType,
 | 
						|
    types.MemberDescriptorType,
 | 
						|
    MethodDescriptorType,
 | 
						|
    WrapperDescriptorType,
 | 
						|
    ClassMethodDescriptorType,
 | 
						|
    staticmethod,
 | 
						|
    classmethod,
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def safe_getattr(obj, name, default=_sentinel):
 | 
						|
    try:
 | 
						|
        attr, is_get_descriptor = getattr_static(obj, name)
 | 
						|
    except AttributeError:
 | 
						|
        if default is _sentinel:
 | 
						|
            raise
 | 
						|
        return default
 | 
						|
    else:
 | 
						|
        if isinstance(attr, ALLOWED_DESCRIPTOR_ACCESS):
 | 
						|
            # In case of descriptors that have get methods we cannot return
 | 
						|
            # it's value, because that would mean code execution.
 | 
						|
            # Since it's an isinstance call, code execution is still possible,
 | 
						|
            # but this is not really a security feature, but much more of a
 | 
						|
            # safety feature. Code execution is basically always possible when
 | 
						|
            # a module is imported. This is here so people don't shoot
 | 
						|
            # themselves in the foot.
 | 
						|
            return getattr(obj, name)
 | 
						|
    return attr
 | 
						|
 | 
						|
 | 
						|
SignatureParam = namedtuple(
 | 
						|
    'SignatureParam',
 | 
						|
    'name has_default default default_string has_annotation annotation annotation_string kind_name'
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def shorten_repr(func):
 | 
						|
    def wrapper(self):
 | 
						|
        r = func(self)
 | 
						|
        if len(r) > 50:
 | 
						|
            r = r[:50] + '..'
 | 
						|
        return r
 | 
						|
    return wrapper
 | 
						|
 | 
						|
 | 
						|
def create_access(inference_state, obj):
 | 
						|
    return inference_state.compiled_subprocess.get_or_create_access_handle(obj)
 | 
						|
 | 
						|
 | 
						|
def load_module(inference_state, dotted_name, sys_path):
 | 
						|
    temp, sys.path = sys.path, sys_path
 | 
						|
    try:
 | 
						|
        __import__(dotted_name)
 | 
						|
    except ImportError:
 | 
						|
        # If a module is "corrupt" or not really a Python module or whatever.
 | 
						|
        warnings.warn(
 | 
						|
            "Module %s not importable in path %s." % (dotted_name, sys_path),
 | 
						|
            UserWarning,
 | 
						|
            stacklevel=2,
 | 
						|
        )
 | 
						|
        return None
 | 
						|
    except Exception:
 | 
						|
        # Since __import__ pretty much makes code execution possible, just
 | 
						|
        # catch any error here and print it.
 | 
						|
        warnings.warn(
 | 
						|
            "Cannot import:\n%s" % traceback.format_exc(), UserWarning, stacklevel=2
 | 
						|
        )
 | 
						|
        return None
 | 
						|
    finally:
 | 
						|
        sys.path = temp
 | 
						|
 | 
						|
    # Just access the cache after import, because of #59 as well as the very
 | 
						|
    # complicated import structure of Python.
 | 
						|
    module = sys.modules[dotted_name]
 | 
						|
    return create_access_path(inference_state, module)
 | 
						|
 | 
						|
 | 
						|
class AccessPath:
 | 
						|
    def __init__(self, accesses):
 | 
						|
        self.accesses = accesses
 | 
						|
 | 
						|
 | 
						|
def create_access_path(inference_state, obj) -> AccessPath:
 | 
						|
    access = create_access(inference_state, obj)
 | 
						|
    return AccessPath(access.get_access_path_tuples())
 | 
						|
 | 
						|
 | 
						|
def get_api_type(obj):
 | 
						|
    if inspect.isclass(obj):
 | 
						|
        return 'class'
 | 
						|
    elif inspect.ismodule(obj):
 | 
						|
        return 'module'
 | 
						|
    elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
 | 
						|
            or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
 | 
						|
        return 'function'
 | 
						|
    # Everything else...
 | 
						|
    return 'instance'
 | 
						|
 | 
						|
 | 
						|
class DirectObjectAccess:
 | 
						|
    def __init__(self, inference_state, obj):
 | 
						|
        self._inference_state = inference_state
 | 
						|
        self._obj = obj
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return '%s(%s)' % (self.__class__.__name__, self.get_repr())
 | 
						|
 | 
						|
    def _create_access(self, obj):
 | 
						|
        return create_access(self._inference_state, obj)
 | 
						|
 | 
						|
    def _create_access_path(self, obj) -> AccessPath:
 | 
						|
        return create_access_path(self._inference_state, obj)
 | 
						|
 | 
						|
    def py__bool__(self):
 | 
						|
        return bool(self._obj)
 | 
						|
 | 
						|
    def py__file__(self) -> Optional[Path]:
 | 
						|
        try:
 | 
						|
            return Path(self._obj.__file__)
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
 | 
						|
    def py__doc__(self):
 | 
						|
        return inspect.getdoc(self._obj) or ''
 | 
						|
 | 
						|
    def py__name__(self):
 | 
						|
        if not _is_class_instance(self._obj) or \
 | 
						|
                inspect.ismethoddescriptor(self._obj):  # slots
 | 
						|
            cls = self._obj
 | 
						|
        else:
 | 
						|
            try:
 | 
						|
                cls = self._obj.__class__
 | 
						|
            except AttributeError:
 | 
						|
                # happens with numpy.core.umath._UFUNC_API (you get it
 | 
						|
                # automatically by doing `import numpy`.
 | 
						|
                return None
 | 
						|
 | 
						|
        try:
 | 
						|
            return cls.__name__
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
 | 
						|
    def py__mro__accesses(self):
 | 
						|
        return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:])
 | 
						|
 | 
						|
    def py__getitem__all_values(self):
 | 
						|
        if isinstance(self._obj, dict):
 | 
						|
            return [self._create_access_path(v) for v in self._obj.values()]
 | 
						|
        if isinstance(self._obj, (list, tuple)):
 | 
						|
            return [self._create_access_path(v) for v in self._obj]
 | 
						|
 | 
						|
        if self.is_instance():
 | 
						|
            cls = DirectObjectAccess(self._inference_state, self._obj.__class__)
 | 
						|
            return cls.py__getitem__all_values()
 | 
						|
 | 
						|
        try:
 | 
						|
            getitem = self._obj.__getitem__
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation()
 | 
						|
            if annotation is not None:
 | 
						|
                return [annotation]
 | 
						|
        return None
 | 
						|
 | 
						|
    def py__simple_getitem__(self, index, *, safe=True):
 | 
						|
        if safe and type(self._obj) not in ALLOWED_GETITEM_TYPES:
 | 
						|
            # Get rid of side effects, we won't call custom `__getitem__`s.
 | 
						|
            return None
 | 
						|
 | 
						|
        return self._create_access_path(self._obj[index])
 | 
						|
 | 
						|
    def py__iter__list(self):
 | 
						|
        try:
 | 
						|
            iter_method = self._obj.__iter__
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
        else:
 | 
						|
            p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation()
 | 
						|
            if p is not None:
 | 
						|
                return [p]
 | 
						|
 | 
						|
        if type(self._obj) not in ALLOWED_GETITEM_TYPES:
 | 
						|
            # Get rid of side effects, we won't call custom `__getitem__`s.
 | 
						|
            return []
 | 
						|
 | 
						|
        lst = []
 | 
						|
        for i, part in enumerate(self._obj):
 | 
						|
            if i > 20:
 | 
						|
                # Should not go crazy with large iterators
 | 
						|
                break
 | 
						|
            lst.append(self._create_access_path(part))
 | 
						|
        return lst
 | 
						|
 | 
						|
    def py__class__(self):
 | 
						|
        return self._create_access_path(self._obj.__class__)
 | 
						|
 | 
						|
    def py__bases__(self):
 | 
						|
        return [self._create_access_path(base) for base in self._obj.__bases__]
 | 
						|
 | 
						|
    def py__path__(self):
 | 
						|
        paths = getattr(self._obj, '__path__', None)
 | 
						|
        # Avoid some weird hacks that would just fail, because they cannot be
 | 
						|
        # used by pickle.
 | 
						|
        if not isinstance(paths, list) \
 | 
						|
                or not all(isinstance(p, str) for p in paths):
 | 
						|
            return None
 | 
						|
        return paths
 | 
						|
 | 
						|
    @shorten_repr
 | 
						|
    def get_repr(self):
 | 
						|
        if inspect.ismodule(self._obj):
 | 
						|
            return repr(self._obj)
 | 
						|
        # Try to avoid execution of the property.
 | 
						|
        if safe_getattr(self._obj, '__module__', default='') == 'builtins':
 | 
						|
            return repr(self._obj)
 | 
						|
 | 
						|
        type_ = type(self._obj)
 | 
						|
        if type_ == type:
 | 
						|
            return type.__repr__(self._obj)
 | 
						|
 | 
						|
        if safe_getattr(type_, '__module__', default='') == 'builtins':
 | 
						|
            # Allow direct execution of repr for builtins.
 | 
						|
            return repr(self._obj)
 | 
						|
        return object.__repr__(self._obj)
 | 
						|
 | 
						|
    def is_class(self):
 | 
						|
        return inspect.isclass(self._obj)
 | 
						|
 | 
						|
    def is_function(self):
 | 
						|
        return inspect.isfunction(self._obj) or inspect.ismethod(self._obj)
 | 
						|
 | 
						|
    def is_module(self):
 | 
						|
        return inspect.ismodule(self._obj)
 | 
						|
 | 
						|
    def is_instance(self):
 | 
						|
        return _is_class_instance(self._obj)
 | 
						|
 | 
						|
    def ismethoddescriptor(self):
 | 
						|
        return inspect.ismethoddescriptor(self._obj)
 | 
						|
 | 
						|
    def get_qualified_names(self):
 | 
						|
        def try_to_get_name(obj):
 | 
						|
            return getattr(obj, '__qualname__', getattr(obj, '__name__', None))
 | 
						|
 | 
						|
        if self.is_module():
 | 
						|
            return ()
 | 
						|
        name = try_to_get_name(self._obj)
 | 
						|
        if name is None:
 | 
						|
            name = try_to_get_name(type(self._obj))
 | 
						|
            if name is None:
 | 
						|
                return ()
 | 
						|
        return tuple(name.split('.'))
 | 
						|
 | 
						|
    def dir(self):
 | 
						|
        return dir(self._obj)
 | 
						|
 | 
						|
    def has_iter(self):
 | 
						|
        try:
 | 
						|
            iter(self._obj)
 | 
						|
            return True
 | 
						|
        except TypeError:
 | 
						|
            return False
 | 
						|
 | 
						|
    def is_allowed_getattr(self, name, safe=True) -> Tuple[bool, bool, Optional[AccessPath]]:
 | 
						|
        # TODO this API is ugly.
 | 
						|
        try:
 | 
						|
            attr, is_get_descriptor = getattr_static(self._obj, name)
 | 
						|
        except AttributeError:
 | 
						|
            if not safe:
 | 
						|
                # Unsafe is mostly used to check for __getattr__/__getattribute__.
 | 
						|
                # getattr_static works for properties, but the underscore methods
 | 
						|
                # are just ignored (because it's safer and avoids more code
 | 
						|
                # execution). See also GH #1378.
 | 
						|
 | 
						|
                # Avoid warnings, see comment in the next function.
 | 
						|
                with warnings.catch_warnings(record=True):
 | 
						|
                    warnings.simplefilter("always")
 | 
						|
                    try:
 | 
						|
                        return hasattr(self._obj, name), False, None
 | 
						|
                    except Exception:
 | 
						|
                        # Obviously has an attribute (probably a property) that
 | 
						|
                        # gets executed, so just avoid all exceptions here.
 | 
						|
                        pass
 | 
						|
            return False, False, None
 | 
						|
        else:
 | 
						|
            if is_get_descriptor and type(attr) not in ALLOWED_DESCRIPTOR_ACCESS:
 | 
						|
                if isinstance(attr, property):
 | 
						|
                    if hasattr(attr.fget, '__annotations__'):
 | 
						|
                        a = DirectObjectAccess(self._inference_state, attr.fget)
 | 
						|
                        return True, True, a.get_return_annotation()
 | 
						|
                # In case of descriptors that have get methods we cannot return
 | 
						|
                # it's value, because that would mean code execution.
 | 
						|
                return True, True, None
 | 
						|
        return True, False, None
 | 
						|
 | 
						|
    def getattr_paths(self, name, default=_sentinel):
 | 
						|
        try:
 | 
						|
            # Make sure no warnings are printed here, this is autocompletion,
 | 
						|
            # warnings should not be shown. See also GH #1383.
 | 
						|
            with warnings.catch_warnings(record=True):
 | 
						|
                warnings.simplefilter("always")
 | 
						|
                return_obj = getattr(self._obj, name)
 | 
						|
        except Exception as e:
 | 
						|
            if default is _sentinel:
 | 
						|
                if isinstance(e, AttributeError):
 | 
						|
                    # Happens e.g. in properties of
 | 
						|
                    # PyQt4.QtGui.QStyleOptionComboBox.currentText
 | 
						|
                    # -> just set it to None
 | 
						|
                    raise
 | 
						|
                # Just in case anything happens, return an AttributeError. It
 | 
						|
                # should not crash.
 | 
						|
                raise AttributeError
 | 
						|
            return_obj = default
 | 
						|
        access = self._create_access(return_obj)
 | 
						|
        if inspect.ismodule(return_obj):
 | 
						|
            return [access]
 | 
						|
 | 
						|
        try:
 | 
						|
            module = return_obj.__module__
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            if module is not None and isinstance(module, str):
 | 
						|
                try:
 | 
						|
                    __import__(module)
 | 
						|
                    # For some modules like _sqlite3, the __module__ for classes is
 | 
						|
                    # different, in this case it's sqlite3. So we have to try to
 | 
						|
                    # load that "original" module, because it's not loaded yet. If
 | 
						|
                    # we don't do that, we don't really have a "parent" module and
 | 
						|
                    # we would fall back to builtins.
 | 
						|
                except ImportError:
 | 
						|
                    pass
 | 
						|
 | 
						|
        module = inspect.getmodule(return_obj)
 | 
						|
        if module is None:
 | 
						|
            module = inspect.getmodule(type(return_obj))
 | 
						|
            if module is None:
 | 
						|
                module = builtins
 | 
						|
        return [self._create_access(module), access]
 | 
						|
 | 
						|
    def get_safe_value(self):
 | 
						|
        if type(self._obj) in (bool, bytes, float, int, str, slice) or self._obj is None:
 | 
						|
            return self._obj
 | 
						|
        raise ValueError("Object is type %s and not simple" % type(self._obj))
 | 
						|
 | 
						|
    def get_api_type(self):
 | 
						|
        return get_api_type(self._obj)
 | 
						|
 | 
						|
    def get_array_type(self):
 | 
						|
        if isinstance(self._obj, dict):
 | 
						|
            return 'dict'
 | 
						|
        return None
 | 
						|
 | 
						|
    def get_key_paths(self):
 | 
						|
        def iter_partial_keys():
 | 
						|
            # We could use list(keys()), but that might take a lot more memory.
 | 
						|
            for (i, k) in enumerate(self._obj.keys()):
 | 
						|
                # Limit key listing at some point. This is artificial, but this
 | 
						|
                # way we don't get stalled because of slow completions
 | 
						|
                if i > 50:
 | 
						|
                    break
 | 
						|
                yield k
 | 
						|
 | 
						|
        return [self._create_access_path(k) for k in iter_partial_keys()]
 | 
						|
 | 
						|
    def get_access_path_tuples(self):
 | 
						|
        accesses = [create_access(self._inference_state, o) for o in self._get_objects_path()]
 | 
						|
        return [(access.py__name__(), access) for access in accesses]
 | 
						|
 | 
						|
    def _get_objects_path(self):
 | 
						|
        def get():
 | 
						|
            obj = self._obj
 | 
						|
            yield obj
 | 
						|
            try:
 | 
						|
                obj = obj.__objclass__
 | 
						|
            except AttributeError:
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                yield obj
 | 
						|
 | 
						|
            try:
 | 
						|
                # Returns a dotted string path.
 | 
						|
                imp_plz = obj.__module__
 | 
						|
            except AttributeError:
 | 
						|
                # Unfortunately in some cases like `int` there's no __module__
 | 
						|
                if not inspect.ismodule(obj):
 | 
						|
                    yield builtins
 | 
						|
            else:
 | 
						|
                if imp_plz is None:
 | 
						|
                    # Happens for example in `(_ for _ in []).send.__module__`.
 | 
						|
                    yield builtins
 | 
						|
                else:
 | 
						|
                    try:
 | 
						|
                        yield sys.modules[imp_plz]
 | 
						|
                    except KeyError:
 | 
						|
                        # __module__ can be something arbitrary that doesn't exist.
 | 
						|
                        yield builtins
 | 
						|
 | 
						|
        return list(reversed(list(get())))
 | 
						|
 | 
						|
    def execute_operation(self, other_access_handle, operator):
 | 
						|
        other_access = other_access_handle.access
 | 
						|
        op = _OPERATORS[operator]
 | 
						|
        return self._create_access_path(op(self._obj, other_access._obj))
 | 
						|
 | 
						|
    def get_annotation_name_and_args(self):
 | 
						|
        """
 | 
						|
        Returns Tuple[Optional[str], Tuple[AccessPath, ...]]
 | 
						|
        """
 | 
						|
        name = None
 | 
						|
        args = ()
 | 
						|
        if safe_getattr(self._obj, '__module__', default='') == 'typing':
 | 
						|
            m = re.match(r'typing.(\w+)\[', repr(self._obj))
 | 
						|
            if m is not None:
 | 
						|
                name = m.group(1)
 | 
						|
 | 
						|
                import typing
 | 
						|
                if sys.version_info >= (3, 8):
 | 
						|
                    args = typing.get_args(self._obj)
 | 
						|
                else:
 | 
						|
                    args = safe_getattr(self._obj, '__args__', default=None)
 | 
						|
        return name, tuple(self._create_access_path(arg) for arg in args)
 | 
						|
 | 
						|
    def needs_type_completions(self):
 | 
						|
        return inspect.isclass(self._obj) and self._obj != type
 | 
						|
 | 
						|
    def _annotation_to_str(self, annotation):
 | 
						|
        return inspect.formatannotation(annotation)
 | 
						|
 | 
						|
    def get_signature_params(self):
 | 
						|
        return [
 | 
						|
            SignatureParam(
 | 
						|
                name=p.name,
 | 
						|
                has_default=p.default is not p.empty,
 | 
						|
                default=self._create_access_path(p.default),
 | 
						|
                default_string=repr(p.default),
 | 
						|
                has_annotation=p.annotation is not p.empty,
 | 
						|
                annotation=self._create_access_path(p.annotation),
 | 
						|
                annotation_string=self._annotation_to_str(p.annotation),
 | 
						|
                kind_name=str(p.kind)
 | 
						|
            ) for p in self._get_signature().parameters.values()
 | 
						|
        ]
 | 
						|
 | 
						|
    def _get_signature(self):
 | 
						|
        obj = self._obj
 | 
						|
        try:
 | 
						|
            return inspect.signature(obj)
 | 
						|
        except (RuntimeError, TypeError):
 | 
						|
            # Reading the code of the function in Python 3.6 implies there are
 | 
						|
            # at least these errors that might occur if something is wrong with
 | 
						|
            # the signature. In that case we just want a simple escape for now.
 | 
						|
            raise ValueError
 | 
						|
 | 
						|
    def get_return_annotation(self) -> Optional[AccessPath]:
 | 
						|
        try:
 | 
						|
            o = self._obj.__annotations__.get('return')
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
 | 
						|
        if o is None:
 | 
						|
            return None
 | 
						|
 | 
						|
        try:
 | 
						|
            o = typing.get_type_hints(self._obj).get('return')
 | 
						|
        except Exception:
 | 
						|
            pass
 | 
						|
 | 
						|
        return self._create_access_path(o)
 | 
						|
 | 
						|
    def negate(self):
 | 
						|
        return self._create_access_path(-self._obj)
 | 
						|
 | 
						|
    def get_dir_infos(self):
 | 
						|
        """
 | 
						|
        Used to return a couple of infos that are needed when accessing the sub
 | 
						|
        objects of an objects
 | 
						|
        """
 | 
						|
        tuples = dict(
 | 
						|
            (name, self.is_allowed_getattr(name))
 | 
						|
            for name in self.dir()
 | 
						|
        )
 | 
						|
        return self.needs_type_completions(), tuples
 | 
						|
 | 
						|
 | 
						|
def _is_class_instance(obj):
 | 
						|
    """Like inspect.* methods."""
 | 
						|
    try:
 | 
						|
        cls = obj.__class__
 | 
						|
    except AttributeError:
 | 
						|
        return False
 | 
						|
    else:
 | 
						|
        # The isinstance check for cls is just there so issubclass doesn't
 | 
						|
        # raise an exception.
 | 
						|
        return cls != type and isinstance(cls, type) and not issubclass(cls, NOT_CLASS_TYPES)
 |