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
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
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
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