diff --git a/jedi/__init__.py b/jedi/__init__.py index 99da97f3..fc540dde 100644 --- a/jedi/__init__.py +++ b/jedi/__init__.py @@ -42,3 +42,6 @@ from jedi.api.environment import find_virtualenvs, find_system_environments, \ get_default_environment, InvalidPythonEnvironment, create_environment, \ get_system_environment from jedi.api.exceptions import InternalError +# Finally load the internal plugins. This is only internal. +from jedi.plugins import registry +del registry diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 27ea1373..3b0a45a7 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -62,8 +62,6 @@ I need to mention now that lazy evaluation is really good because it only *evaluates* what needs to be *evaluated*. All the statements and modules that are not used are just being ignored. """ -from functools import partial - from parso.python import tree import parso from parso import python_bytes_to_unicode @@ -84,14 +82,7 @@ from jedi.evaluate.context import ClassContext, FunctionContext, \ from jedi.evaluate.context.iterable import CompForContext from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \ eval_node, check_tuple_assignments - - -def _execute(context, arguments): - debug.dbg('execute: %s %s', context, arguments) - with debug.increase_indent_cm(): - context_set = context.py__call__(arguments=arguments) - debug.dbg('execute result: %s in %s', context_set, context) - return context_set +from jedi.plugins import plugin_manager class Evaluator(object): @@ -119,24 +110,21 @@ class Evaluator(object): self.reset_recursion_limitations() self.allow_different_encoding = True - # Plugin API - from jedi.plugins import plugin_manager - plugin_callbacks = plugin_manager.get_callbacks() - self.execute = plugin_callbacks.decorate('execute', callback=_execute) - self._import_module = partial( - plugin_callbacks.decorate( - 'import_module', - callback=imports.import_module - ), - self, - ) - def import_module(self, import_names, parent_module_context=None, sys_path=None, prefer_stubs=True): if sys_path is None: sys_path = self.get_sys_path() - return self._import_module(import_names, parent_module_context, - sys_path, prefer_stubs=prefer_stubs) + return imports.import_module(self, import_names, parent_module_context, + sys_path, prefer_stubs=prefer_stubs) + + @staticmethod + @plugin_manager.decorate() + def execute(context, arguments): + debug.dbg('execute: %s %s', context, arguments) + with debug.increase_indent_cm(): + context_set = context.py__call__(arguments=arguments) + debug.dbg('execute result: %s in %s', context_set, context) + return context_set @property @evaluator_function_cache() diff --git a/jedi/evaluate/gradual/typeshed.py b/jedi/evaluate/gradual/typeshed.py index 1fc8d323..5a386c05 100644 --- a/jedi/evaluate/gradual/typeshed.py +++ b/jedi/evaluate/gradual/typeshed.py @@ -1,5 +1,6 @@ import os import re +from functools import wraps from jedi.file_io import FileIO from jedi._compatibility import FileNotFoundError, cast_path @@ -87,6 +88,7 @@ def _cache_stub_file_map(version_info): def import_module_decorator(func): + @wraps(func) def wrapper(evaluator, import_names, parent_module_context, sys_path, prefer_stubs): try: python_context_set = evaluator.module_cache.get(import_names) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index fadfe5f4..f7ec8c31 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -21,7 +21,7 @@ from jedi._compatibility import (FileNotFoundError, ImplicitNSInfo, force_unicode, unicode) from jedi import debug from jedi import settings -from jedi.file_io import KnownContentFileIO, FolderIO, FileIO +from jedi.file_io import KnownContentFileIO, FileIO from jedi.parser_utils import get_cached_code_lines from jedi.evaluate import sys_path from jedi.evaluate import helpers @@ -33,6 +33,7 @@ from jedi.evaluate.names import ImportName, SubModuleName from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS from jedi.evaluate.gradual.typeshed import import_module_decorator from jedi.evaluate.context.module import iter_module_names +from jedi.plugins import plugin_manager class ModuleCache(object): @@ -371,6 +372,7 @@ class Importer(object): return names +@plugin_manager.decorate() @import_module_decorator def import_module(evaluator, import_names, parent_module_context, sys_path): """ diff --git a/jedi/plugins/__init__.py b/jedi/plugins/__init__.py index 991f8a03..df106cfc 100644 --- a/jedi/plugins/__init__.py +++ b/jedi/plugins/__init__.py @@ -1,39 +1,47 @@ -from jedi.plugins import stdlib -from jedi.plugins import flask +from functools import wraps class _PluginManager(object): - def __init__(self, registered_plugin_classes=()): - self._registered_plugin_classes = list(registered_plugin_classes) + def __init__(self): + self._registered_plugins = [] + self._cached_base_callbacks = {} + self._built_functions = {} - def register(self, plugin_class): + def register(self, *plugins): """ Makes it possible to register your plugin. """ - self._registered_plugins.append(plugin_class) + self._registered_plugins.extend(plugins) + self._build_functions() - def _build_chain(self): - for plugin_class in self._registered_plugin_classes: - yield plugin_class + def decorate(self): + def decorator(callback): + @wraps(callback) + def wrapper(*args, **kwargs): + return built_functions[name](*args, **kwargs) - def get_callbacks(self): - return _PluginCallbacks(self._build_chain()) + name = callback.__name__ + + assert name not in self._built_functions + built_functions = self._built_functions + built_functions[name] = callback + self._cached_base_callbacks[name] = callback + + return wrapper + + return decorator + + def _build_functions(self): + for name, callback in self._cached_base_callbacks.items(): + for plugin in reversed(self._registered_plugins): + # Need to reverse so the first plugin is run first. + try: + func = getattr(plugin, name) + except AttributeError: + pass + else: + callback = func(callback) + self._built_functions[name] = callback -class _PluginCallbacks(object): - def __init__(self, plugins): - self._plugins = list(plugins) - - def decorate(self, name, callback): - for plugin in reversed(self._plugins): - # Need to reverse so the first plugin is run first. - try: - func = getattr(plugin, name) - except AttributeError: - pass - else: - callback = func(callback) - return callback - - -plugin_manager = _PluginManager([stdlib, flask]) +plugin_manager = _PluginManager() diff --git a/jedi/plugins/registry.py b/jedi/plugins/registry.py new file mode 100644 index 00000000..23913244 --- /dev/null +++ b/jedi/plugins/registry.py @@ -0,0 +1,10 @@ +""" +This is not a plugin, this is just the place were plugins are registered. +""" + +from jedi.plugins import stdlib +from jedi.plugins import flask +from jedi.plugins import plugin_manager + + +plugin_manager.register(stdlib, flask)