Esempio n. 1
0
    def _loop(self):
        from jedi.evaluate.sys_path import _get_venv_sitepackages

        while True:
            data = stream_read(sys.stdin)
            if not isinstance(data, tuple):
                continue

            cache_key, source, line, col, filename, options = data
            orig_path = sys.path[:]
            venv = os.getenv('VIRTUAL_ENV')
            if venv:
                sys.path.insert(0, _get_venv_sitepackages(venv))
            add_path = self.find_extra_sys_path(filename)
            if add_path and add_path not in sys.path:
                # Add the found path to sys.path.  I'm not 100% certain if this
                # is actually helping anything, but it feels like the right
                # thing to do.
                sys.path.insert(0, add_path)
            if filename:
                sys.path.append(os.path.dirname(filename))

            if isinstance(options, dict):
                extra = options.get('extra_path')
                if extra:
                    if not isinstance(extra, list):
                        extra = [extra]
                    sys.path.extend(extra)

                # Add extra paths if working on a Python remote plugin.
                sys.path.extend(utils.rplugin_runtime_paths(options))

            # Decorators on incomplete functions cause an error to be raised by
            # Jedi.  I assume this is because Jedi is attempting to evaluate
            # the return value of the wrapped, but broken, function.
            # Our solution is to simply strip decorators from the source since
            # we are a completion service, not the syntax police.
            out = None

            if cache_key[-1] == 'vars':
                # Attempt scope completion.  If it fails, it should fall
                # through to script completion.
                out = self.scoped_completions(source, filename, cache_key[-2])

            if not out:
                out = self.script_completion(source, line, col, filename)

            if not out and cache_key[-1] in ('package', 'local'):
                # The backup plan
                try:
                    out = self.module_completions(cache_key[0], sys.path)
                except Exception:
                    pass

            stream_write(sys.stdout, out)
            sys.path[:] = orig_path
Esempio n. 2
0
    def _loop(self):
        while True:
            data = stream_read(sys.stdin)
            if not isinstance(data, tuple):
                continue

            cache_key, source, line, col, filename, options = data
            orig_path = sys.path[:]
            add_path = self.find_extra_sys_path(filename)
            if add_path and add_path not in sys.path:
                # Add the found path to sys.path.  I'm not 100% certain if this
                # is actually helping anything, but it feels like the right
                # thing to do.
                sys.path.insert(0, add_path)
            if filename:
                sys.path.append(os.path.dirname(filename))

            if isinstance(options, dict):
                extra = options.get('extra_path')
                if extra:
                    if not isinstance(extra, list):
                        extra = [extra]
                    sys.path.extend(extra)

                # Add extra paths if working on a Python remote plugin.
                sys.path.extend(utils.rplugin_runtime_paths(options))

            out = self.script_completion(source, line, col, filename)

            if not out and cache_key[-1] == 'vars':
                log.debug('Fallback to scoped completions')
                out = self.scoped_completions(source, filename, cache_key[-2])

            if not out and isinstance(options,
                                      dict) and 'synthetic' in options:
                synthetic = options.get('synthetic')
                log.debug('Using synthetic completion: %r', synthetic)
                out = self.script_completion(synthetic['src'],
                                             synthetic['line'],
                                             synthetic['col'], filename)

            if not out and cache_key[-1] in ('package', 'local'):
                # The backup plan
                # TODO(blueyed): remove this (far too less results for e.g.
                # numpy), or at least do not cache it to disk.
                log.debug('Fallback to module completions')
                try:
                    out = self.module_completions(cache_key[0], sys.path)
                except Exception:
                    pass

            stream_write(sys.stdout, out)
            sys.path[:] = orig_path
Esempio n. 3
0
def cache_context(filename, context, source, extra_path):  # noqa: C901
    """Caching based on context input.

    If the input is blank, it was triggered with `.` to get module completions.

    The module files as reported by Jedi are stored with their modification
    times to help detect if a cache needs to be refreshed.

    For scoped variables in the buffer, construct a cache key using the
    filename.  The buffer file's modification time is checked to see if the
    completion needs to be refreshed.  The approximate scope lines are cached
    to help invalidate the cache based on line position.

    Cache keys are made using tuples to make them easier to interpret later.
    """
    cinput = context['input'].lstrip().lstrip('@')
    if not re.sub(r'[\s\d\.]+', '', cinput):
        return None, []
    filename_hash = hashlib.md5(filename.encode('utf8')).hexdigest()
    line = context['position'][1]
    log.debug('Input: "%s"', cinput)
    cache_key = None
    extra_modules = []
    cur_module = os.path.splitext(os.path.basename(filename))[0]

    if cinput.startswith(('import ', 'from ')):
        # Cache imports with buffer filename as the key prefix.
        # For `from` imports, the first part of the statement is
        # considered to be the same as `import` for caching.

        import_key = 'import~'
        cinput = context['input'].lstrip()
        m = re.search(r'^from\s+(\S+)(.*)', cinput)
        if m:
            if m.group(2).lstrip() in 'import':
                cache_key = ('importkeyword~', )
                return cache_key, extra_modules
            import_key = m.group(1) or 'import~'
        elif cinput.startswith('import ') and cinput.rstrip().endswith('.'):
            import_key = re.sub(r'[^\s\w\.]', ' ', cinput.strip()).split()[-1]

        if import_key:
            if '.' in import_key and import_key[-1] not in whitespace \
                    and not re.search(r'^from\s+\S+\s+import', cinput):
                # Dot completion on the import line
                import_key, _ = import_key.rsplit('.', 1)
            import_key = import_key.rstrip('.')
            module_file = utils.module_search(
                import_key,
                chain(extra_path,
                      [context.get('cwd'), os.path.dirname(filename)],
                      utils.rplugin_runtime_paths(context)))
            if module_file:
                cache_key = (import_key, 'local')
                extra_modules.append(module_file)
            elif is_package(import_key):
                cache_key = (import_key, 'package')
            elif not cinput.endswith('.'):
                cache_key = ('import~',)
            else:
                return None, extra_modules

    if not cache_key:
        obj = split_module(cinput.strip())
        if obj:
            cache_key = (obj, 'package')
            if obj.startswith('self'):
                if os.path.exists(filename):
                    extra_modules.append(filename)
                # `self` is a special case object that needs a scope included
                # in the cache key.
                parents = get_parents(source, line, class_only=True)
                parents.insert(0, cur_module)
                cache_key = (filename_hash, tuple(parents), obj)
            else:
                module_path = full_module(source, obj)
                if module_path and not module_path.startswith('.') \
                        and is_package(module_path):
                    cache_key = (module_path, 'package')
                else:
                    # A quick scan revealed that the dot completion doesn't
                    # involve an imported module.  Treat it like a scoped
                    # variable and ensure the cache invalidates when the file
                    # is saved.
                    if os.path.exists(filename):
                        extra_modules.append(filename)

                    module_file = utils.module_search(module_path,
                                                      [os.path.dirname(filename)])
                    if module_file:
                        cache_key = (module_path, 'local')
                    else:
                        parents = get_parents(source, line)
                        parents.insert(0, cur_module)
                        cache_key = (filename_hash, tuple(parents), obj, 'dot')
        elif context.get('complete_str') or cinput.rstrip().endswith('='):
            parents = get_parents(source, line)
            parents.insert(0, cur_module)
            cache_key = (filename_hash, tuple(parents), 'vars')
            if os.path.exists(filename):
                extra_modules.append(filename)

    return cache_key, extra_modules
Esempio n. 4
0
    def _loop(self):
        from jedi.evaluate.sys_path import _get_venv_sitepackages

        while True:
            data = stream_read(sys.stdin)
            if not isinstance(data, tuple):
                continue

            cache_key, source, line, col, filename, options = data
            orig_path = sys.path[:]
            venv = os.getenv('VIRTUAL_ENV')
            if venv:
                sys.path.insert(0, _get_venv_sitepackages(venv))
            add_path = self.find_extra_sys_path(filename)
            if add_path and add_path not in sys.path:
                # Add the found path to sys.path.  I'm not 100% certain if this
                # is actually helping anything, but it feels like the right
                # thing to do.
                sys.path.insert(0, add_path)
            if filename:
                sys.path.append(os.path.dirname(filename))

            if isinstance(options, dict):
                extra = options.get('extra_path')
                if extra:
                    if not isinstance(extra, list):
                        extra = [extra]
                    sys.path.extend(extra)

                # Add extra paths if working on a Python remote plugin.
                sys.path.extend(utils.rplugin_runtime_paths(options))

            # Decorators on incomplete functions cause an error to be raised by
            # Jedi.  I assume this is because Jedi is attempting to evaluate
            # the return value of the wrapped, but broken, function.
            # Our solution is to simply strip decorators from the source since
            # we are a completion service, not the syntax police.
            out = self.script_completion(source, line, col, filename)

            if not out and cache_key[-1] == 'vars':
                # Attempt scope completion.  If it fails, it should fall
                # through to script completion.
                log.debug('Fallback to scoped completions')
                out = self.scoped_completions(source, filename, cache_key[-2])

            if not out and isinstance(options,
                                      dict) and 'synthetic' in options:
                synthetic = options.get('synthetic')
                log.debug('Using synthetic completion: %r', synthetic)
                out = self.script_completion(synthetic['src'],
                                             synthetic['line'],
                                             synthetic['col'], filename)

            if not out and cache_key[-1] in ('package', 'local'):
                # The backup plan
                log.debug('Fallback to module completions')
                try:
                    out = self.module_completions(cache_key[0], sys.path)
                except Exception:
                    pass

            stream_write(sys.stdout, out)
            sys.path[:] = orig_path