From 3d55b2d826bc5fbac5ab8d42869519290d8d6d72 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 12:50:17 +0200 Subject: [PATCH 01/11] Subprocess error reporting improvements --- jedi/evaluate/compiled/subprocess/__init__.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/jedi/evaluate/compiled/subprocess/__init__.py b/jedi/evaluate/compiled/subprocess/__init__.py index 5a9a52fd..420e048f 100644 --- a/jedi/evaluate/compiled/subprocess/__init__.py +++ b/jedi/evaluate/compiled/subprocess/__init__.py @@ -22,7 +22,7 @@ except ImportError: from Queue import Queue, Empty # python 2.7 from jedi._compatibility import queue, is_py3, force_unicode, \ - pickle_dump, pickle_load, GeneralizedPopen + pickle_dump, pickle_load, GeneralizedPopen, print_to_stderr from jedi import debug from jedi.cache import memoize_method from jedi.evaluate.compiled.subprocess import functions @@ -40,6 +40,18 @@ def _enqueue_output(out, queue): out.close() +def _add_stderr_to_debug(stderr_queue): + while True: + # Try to do some error reporting from the subprocess and print its + # stderr contents. + try: + line = stderr_queue.get_nowait() + line = line.decode('utf-8', 'replace') + debug.warning('stderr output: %s' % line.rstrip('\n')) + except Empty: + break + + def _get_function(name): return getattr(functions, name) @@ -229,10 +241,11 @@ class CompiledSubprocess(object): is_exception, traceback, result = pickle_load(self._process.stdout) except EOFError as eof_error: try: - stderr = self._process.stderr.read() + stderr = self._process.stderr.read().decode('utf-8', 'replace') except Exception as exc: stderr = '' % exc self._kill() + _add_stderr_to_debug(self._stderr_queue) raise InternalError( "The subprocess %s has crashed (%r, stderr=%s)." % ( self._executable, @@ -240,15 +253,7 @@ class CompiledSubprocess(object): stderr, )) - while True: - # Try to do some error reporting from the subprocess and print its - # stderr contents. - try: - line = self._stderr_queue.get_nowait() - line = line.decode('utf-8', 'replace') - debug.warning('stderr output: %s' % line.rstrip('\n')) - except Empty: - break + _add_stderr_to_debug(self._stderr_queue) if is_exception: # Replace the attribute error message with a the traceback. It's From 1456a156a6aa4f42dcdd9c93b07224956bd5cf81 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 13:53:57 +0200 Subject: [PATCH 02/11] get_params -> get_executed_params where possible --- jedi/evaluate/arguments.py | 8 ++++---- jedi/evaluate/context/function.py | 2 +- jedi/evaluate/context/instance.py | 3 +++ jedi/evaluate/param.py | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/jedi/evaluate/arguments.py b/jedi/evaluate/arguments.py index b7a9c8eb..075ce50a 100644 --- a/jedi/evaluate/arguments.py +++ b/jedi/evaluate/arguments.py @@ -8,7 +8,7 @@ from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts, \ from jedi.evaluate.filters import ParamName from jedi.evaluate.base_context import NO_CONTEXTS from jedi.evaluate.context import iterable -from jedi.evaluate.param import get_params, ExecutedParam +from jedi.evaluate.param import get_executed_params, ExecutedParam def try_iter_content(types, depth=0): @@ -69,12 +69,12 @@ class AbstractArguments(object): def unpack(self, funcdef=None): raise NotImplementedError - def get_params(self, execution_context): - return get_params(execution_context, self) + def get_executed_params(self, execution_context): + return get_executed_params(execution_context, self) class AnonymousArguments(AbstractArguments): - def get_params(self, execution_context): + def get_executed_params(self, execution_context): from jedi.evaluate.dynamic import search_params return search_params( execution_context.evaluator, diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 992cecdf..20be8333 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -247,4 +247,4 @@ class FunctionExecutionContext(TreeContext): @evaluator_method_cache() def get_params(self): - return self.var_args.get_params(self) + return self.var_args.get_executed_params(self) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 0ce52407..ffc07d91 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -334,6 +334,9 @@ class BoundMethod(AbstractFunction): return InstanceFunctionExecution( self._instance, self.parent_context, self, arguments) + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self._function) + class CompiledBoundMethod(compiled.CompiledObject): def __init__(self, func): diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 1445ef0c..84f281e5 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -41,7 +41,7 @@ class ExecutedParam(object): return '<%s: %s>' % (self.__class__.__name__, self.string_name) -def get_params(execution_context, var_args): +def get_executed_params(execution_context, var_args): result_params = [] param_dict = {} funcdef = execution_context.tree_node From 7d16a35693127ac2a08358c770945ad9df770f35 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 13:58:06 +0200 Subject: [PATCH 03/11] Also move the remaining get_params to get_executed_params Remove the class's get_params entirely, because it is apparently not needed and contained a funny return. --- jedi/evaluate/context/function.py | 2 +- jedi/evaluate/context/klass.py | 5 ----- jedi/evaluate/dynamic.py | 4 ++-- jedi/evaluate/filters.py | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 20be8333..c982f055 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -246,5 +246,5 @@ class FunctionExecutionContext(TreeContext): origin_scope=origin_scope) @evaluator_method_cache() - def get_params(self): + def get_executed_params(self): return self.var_args.get_executed_params(self) diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index 57129a14..60f7414c 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -175,11 +175,6 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): def py__class__(self): return compiled.builtin_from_name(self.evaluator, u'type') - def get_params(self): - from jedi.evaluate.context import AnonymousInstance - anon = AnonymousInstance(self.evaluator, self.parent_context, self) - return [AnonymousInstanceParamName(anon, param.name) for param in self.funcdef.get_params()] - def get_filters(self, search_global, until_position=None, origin_scope=None, is_instance=False): if search_global: yield ParserTreeFilter( diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index 0fd65525..7f7b0d87 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -99,7 +99,7 @@ def search_params(evaluator, execution_context, funcdef): ) if function_executions: zipped_params = zip(*list( - function_execution.get_params() + function_execution.get_executed_params() for function_execution in function_executions )) params = [DynamicExecutedParams(evaluator, executed_params) for executed_params in zipped_params] @@ -208,7 +208,7 @@ def _check_name_for_execution(evaluator, context, compare_node, name, trailer): # Here we're trying to find decorators by checking the first # parameter. It's not very generic though. Should find a better # solution that also applies to nested decorators. - params = value.parent_context.get_params() + params = value.parent_context.get_executed_params() if len(params) != 1: continue values = params[0].infer() diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index fd41b55a..a5cdfdbe 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -136,7 +136,7 @@ class ParamName(AbstractTreeName): return self.get_param().infer() def get_param(self): - params = self.parent_context.get_params() + params = self.parent_context.get_executed_params() param_node = search_ancestor(self.tree_name, 'param') return params[param_node.position_index] From e17d7f5d427d9d9fbbafe4511947c0b47fd65da0 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 14:17:46 +0200 Subject: [PATCH 04/11] Don't use arguments that are not needed --- jedi/evaluate/context/instance.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index ffc07d91..c0f6d32d 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -25,7 +25,7 @@ class BaseInstanceFunctionExecution(FunctionExecutionContext): class InstanceFunctionExecution(BaseInstanceFunctionExecution): def __init__(self, instance, parent_context, function_context, var_args): - var_args = InstanceVarArgs(self, var_args) + var_args = InstanceVarArgs(instance, var_args) super(InstanceFunctionExecution, self).__init__( instance, parent_context, function_context, var_args) @@ -450,8 +450,8 @@ class SelfAttributeFilter(ClassFilter): class InstanceVarArgs(AbstractArguments): - def __init__(self, execution_context, var_args): - self._execution_context = execution_context + def __init__(self, instance, var_args): + self._instance = instance self._var_args = var_args @memoize_method @@ -467,7 +467,7 @@ class InstanceVarArgs(AbstractArguments): return self._var_args.trailer def unpack(self, func=None): - yield None, LazyKnownContext(self._execution_context.instance) + yield None, LazyKnownContext(self._instance) for values in self._get_var_args().unpack(func): yield values From 0101fdd9daff854cd0bb8969d281f757e6d96027 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 14:18:56 +0200 Subject: [PATCH 05/11] Remove old garbage code --- jedi/evaluate/context/instance.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index c0f6d32d..c806e75c 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -8,7 +8,6 @@ from jedi.evaluate.base_context import Context, NO_CONTEXTS, ContextSet, \ from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments -from jedi.cache import memoize_method from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter @@ -454,10 +453,6 @@ class InstanceVarArgs(AbstractArguments): self._instance = instance self._var_args = var_args - @memoize_method - def _get_var_args(self): - return self._var_args - @property def argument_node(self): return self._var_args.argument_node @@ -468,8 +463,8 @@ class InstanceVarArgs(AbstractArguments): def unpack(self, func=None): yield None, LazyKnownContext(self._instance) - for values in self._get_var_args().unpack(func): + for values in self._var_args.unpack(func): yield values def get_calling_nodes(self): - return self._get_var_args().get_calling_nodes() + return self._var_args.get_calling_nodes() From 8cae517821884dffbe89439ff0d77d0cf7190b52 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 14:34:44 +0200 Subject: [PATCH 06/11] Use InstanceArguments directly and not via InstanceFunctionExecution --- jedi/evaluate/context/instance.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index c806e75c..3c08bfab 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -24,7 +24,7 @@ class BaseInstanceFunctionExecution(FunctionExecutionContext): class InstanceFunctionExecution(BaseInstanceFunctionExecution): def __init__(self, instance, parent_context, function_context, var_args): - var_args = InstanceVarArgs(instance, var_args) + var_args = InstanceArguments(instance, var_args) super(InstanceFunctionExecution, self).__init__( instance, parent_context, function_context, var_args) @@ -330,8 +330,12 @@ class BoundMethod(AbstractFunction): return AnonymousInstanceFunctionExecution( self._instance, self.parent_context, self, arguments) else: - return InstanceFunctionExecution( - self._instance, self.parent_context, self, arguments) + return FunctionExecutionContext( + self.evaluator, + self.parent_context, + self, + InstanceArguments(self._instance, arguments) + ) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self._function) @@ -448,7 +452,7 @@ class SelfAttributeFilter(ClassFilter): return names -class InstanceVarArgs(AbstractArguments): +class InstanceArguments(AbstractArguments): def __init__(self, instance, var_args): self._instance = instance self._var_args = var_args From 357c86ad9cd808af5bd917bb469f845bd4e05608 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 14:57:00 +0200 Subject: [PATCH 07/11] Use the InstanceArguments for super as well --- jedi/evaluate/context/instance.py | 4 ++-- jedi/evaluate/stdlib.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 3c08bfab..35b32936 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -454,7 +454,7 @@ class SelfAttributeFilter(ClassFilter): class InstanceArguments(AbstractArguments): def __init__(self, instance, var_args): - self._instance = instance + self.instance = instance self._var_args = var_args @property @@ -466,7 +466,7 @@ class InstanceArguments(AbstractArguments): return self._var_args.trailer def unpack(self, func=None): - yield None, LazyKnownContext(self._instance) + yield None, LazyKnownContext(self.instance) for values in self._var_args.unpack(func): yield values diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index 06296d93..93977189 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -18,12 +18,12 @@ from jedi import debug from jedi.evaluate.arguments import ValuesArguments from jedi.evaluate import analysis from jedi.evaluate import compiled -from jedi.evaluate.context.instance import InstanceFunctionExecution, \ +from jedi.evaluate.context.instance import \ AbstractInstanceContext, CompiledInstance, BoundMethod, \ - AnonymousInstanceFunctionExecution + AnonymousInstanceFunctionExecution, InstanceArguments from jedi.evaluate.base_context import ContextualizedNode, \ NO_CONTEXTS, ContextSet -from jedi.evaluate.context import ClassContext, ModuleContext +from jedi.evaluate.context import ClassContext, ModuleContext, FunctionExecutionContext from jedi.evaluate.context import iterable from jedi.evaluate.lazy_context import LazyTreeContext from jedi.evaluate.syntax_tree import is_string @@ -187,10 +187,15 @@ class SuperInstance(AbstractInstanceContext): @argument_clinic('[type[, obj]], /', want_context=True) def builtins_super(evaluator, types, objects, context): # TODO make this able to detect multiple inheritance super - if isinstance(context, (InstanceFunctionExecution, - AnonymousInstanceFunctionExecution)): + if isinstance(context, FunctionExecutionContext): + if isinstance(context.var_args, InstanceArguments): + su = context.var_args.instance.py__class__().py__bases__() + return su[0].infer().execute_evaluated() + + if isinstance(context, AnonymousInstanceFunctionExecution): su = context.instance.py__class__().py__bases__() return su[0].infer().execute_evaluated() + return NO_CONTEXTS From 10ecb77673cf3757c8267e645a7a07359e17d6ab Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 23:26:15 +0200 Subject: [PATCH 08/11] Get rid of InstanceFunctionExecution, because it's really not needed --- jedi/evaluate/context/instance.py | 57 +++++++++++++------------------ jedi/evaluate/context/iterable.py | 11 +++--- jedi/evaluate/pep0484.py | 4 +-- jedi/evaluate/syntax_tree.py | 2 +- 4 files changed, 30 insertions(+), 44 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 35b32936..b38ac085 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -1,6 +1,7 @@ from abc import abstractproperty from jedi import debug +from jedi import settings from jedi.evaluate import compiled from jedi.evaluate import filters from jedi.evaluate.base_context import Context, NO_CONTEXTS, ContextSet, \ @@ -15,27 +16,13 @@ from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope -class BaseInstanceFunctionExecution(FunctionExecutionContext): - def __init__(self, instance, *args, **kwargs): - self.instance = instance - super(BaseInstanceFunctionExecution, self).__init__( - instance.evaluator, *args, **kwargs) - - -class InstanceFunctionExecution(BaseInstanceFunctionExecution): - def __init__(self, instance, parent_context, function_context, var_args): - var_args = InstanceArguments(instance, var_args) - - super(InstanceFunctionExecution, self).__init__( - instance, parent_context, function_context, var_args) - - -class AnonymousInstanceFunctionExecution(BaseInstanceFunctionExecution): +class AnonymousInstanceFunctionExecution(FunctionExecutionContext): function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter - def __init__(self, instance, parent_context, function_context, var_args): + def __init__(self, instance, *args, **kwargs): + self.instance = instance super(AnonymousInstanceFunctionExecution, self).__init__( - instance, parent_context, function_context, var_args) + instance.evaluator, *args, **kwargs) class AbstractInstanceContext(Context): @@ -43,7 +30,6 @@ class AbstractInstanceContext(Context): This class is used to evaluate instances. """ api_type = u'instance' - function_execution_cls = InstanceFunctionExecution def __init__(self, evaluator, parent_context, class_context, var_args): super(AbstractInstanceContext, self).__init__(evaluator, parent_context) @@ -159,12 +145,7 @@ class AbstractInstanceContext(Context): pass def _create_init_execution(self, class_context, bound_method): - return self.function_execution_cls( - self, - class_context.parent_context, - bound_method, - self.var_args - ) + return bound_method.get_function_execution(self.var_args) def create_init_executions(self): for name in self.get_function_slot_names(u'__init__'): @@ -211,16 +192,18 @@ class AbstractInstanceContext(Context): class CompiledInstance(AbstractInstanceContext): - def __init__(self, *args, **kwargs): - super(CompiledInstance, self).__init__(*args, **kwargs) + def __init__(self, evaluator, parent_context, class_context, var_args): + self._original_var_args = var_args + # I don't think that dynamic append lookups should happen here. That # sounds more like something that should go to py__iter__. - self._original_var_args = self.var_args - - if self.class_context.name.string_name in ['list', 'set'] \ - and self.parent_context.get_root_context() == self.evaluator.builtins_module: + if class_context.py__name__() in ['list', 'set'] \ + and parent_context.get_root_context() == evaluator.builtins_module: # compare the module path with the builtin name. - self.var_args = iterable.get_dynamic_array_instance(self) + if settings.dynamic_array_additions: + var_args = iterable.get_dynamic_array_instance(self, var_args) + + super(CompiledInstance, self).__init__(evaluator, parent_context, class_context, var_args) @property def name(self): @@ -252,8 +235,6 @@ class TreeInstance(AbstractInstanceContext): class AnonymousInstance(TreeInstance): - function_execution_cls = AnonymousInstanceFunctionExecution - def __init__(self, evaluator, parent_context, class_context): super(AnonymousInstance, self).__init__( evaluator, @@ -262,6 +243,14 @@ class AnonymousInstance(TreeInstance): var_args=AnonymousArguments(), ) + def _create_init_execution(self, class_context, bound_method): + return AnonymousInstanceFunctionExecution( + self, + class_context.parent_context, + bound_method, + self.var_args + ) + class CompiledInstanceName(compiled.CompiledName): diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index 104d158c..31011e36 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -630,12 +630,9 @@ def _check_array_additions(context, sequence): return added_types -def get_dynamic_array_instance(instance): +def get_dynamic_array_instance(instance, arguments): """Used for set() and list() instances.""" - if not settings.dynamic_array_additions: - return instance.var_args - - ai = _ArrayInstance(instance) + ai = _ArrayInstance(instance, arguments) from jedi.evaluate import arguments return arguments.ValuesArguments([ContextSet(ai)]) @@ -651,9 +648,9 @@ class _ArrayInstance(object): and therefore doesn't need filters, `py__bool__` and so on, because we don't use these operations in `builtins.py`. """ - def __init__(self, instance): + def __init__(self, instance, var_args): self.instance = instance - self.var_args = instance.var_args + self.var_args = var_args def py__iter__(self): var_args = self.var_args diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index f23943e1..0b96cbc3 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -157,8 +157,8 @@ def infer_param(execution_context, param): "Comments length != Params length %s %s", params_comments, all_params ) - from jedi.evaluate.context.instance import BaseInstanceFunctionExecution - if isinstance(execution_context, BaseInstanceFunctionExecution): + from jedi.evaluate.context.instance import InstanceArguments + if isinstance(execution_context.var_args, InstanceArguments): if index == 0: # Assume it's self, which is already handled return NO_CONTEXTS diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index 614c0033..dc7ea0a7 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -270,7 +270,7 @@ def eval_expr_stmt(context, stmt, seek_name=None): # necessary. if not allowed and context.get_root_context() == context.evaluator.builtins_module: try: - instance = context.instance + instance = context.var_args.instance except AttributeError: pass else: From 38a22a4ae80592abddbd292418e96d67df086189 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 5 Aug 2018 23:37:46 +0200 Subject: [PATCH 09/11] Move some anonymous instance function execution stuff --- jedi/evaluate/context/instance.py | 22 +++++++++++++++++++++- jedi/evaluate/context/klass.py | 2 +- jedi/evaluate/filters.py | 17 ----------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index b38ac085..dc2c0f52 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -1,5 +1,7 @@ from abc import abstractproperty +from parso.tree import search_ancestor + from jedi import debug from jedi import settings from jedi.evaluate import compiled @@ -11,13 +13,31 @@ from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction +from jedi.evaluate.filters import FunctionExecutionFilter, ParamName from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope +class _AnonymousInstanceParamName(ParamName): + def infer(self): + param_node = search_ancestor(self.tree_name, 'param') + # TODO I think this should not belong here. It's not even really true, + # because classmethod and other descriptors can change it. + if param_node.position_index == 0: + # This is a speed optimization, to return the self param (because + # it's known). This only affects anonymous instances. + return ContextSet(self.parent_context.instance) + else: + return self.get_param().infer() + + +class _AnonymousInstanceFunctionExecutionFilter(FunctionExecutionFilter): + param_name = _AnonymousInstanceParamName + + class AnonymousInstanceFunctionExecution(FunctionExecutionContext): - function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter + function_execution_filter = _AnonymousInstanceFunctionExecutionFilter def __init__(self, instance, *args, **kwargs): self.instance = instance diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index 60f7414c..b676ffdb 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -43,7 +43,7 @@ from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass from jedi.evaluate import compiled from jedi.evaluate.lazy_context import LazyKnownContext from jedi.evaluate.filters import ParserTreeFilter, TreeNameDefinition, \ - ContextName, AnonymousInstanceParamName + ContextName from jedi.evaluate.base_context import ContextSet, iterator_to_context_set, \ TreeContext diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index a5cdfdbe..77474e2d 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -141,19 +141,6 @@ class ParamName(AbstractTreeName): return params[param_node.position_index] -class AnonymousInstanceParamName(ParamName): - def infer(self): - param_node = search_ancestor(self.tree_name, 'param') - # TODO I think this should not belong here. It's not even really true, - # because classmethod and other descriptors can change it. - if param_node.position_index == 0: - # This is a speed optimization, to return the self param (because - # it's known). This only affects anonymous instances. - return ContextSet(self.parent_context.instance) - else: - return self.get_param().infer() - - class AbstractFilter(object): _until_position = None @@ -266,10 +253,6 @@ class FunctionExecutionFilter(ParserTreeFilter): yield TreeNameDefinition(self.context, name) -class AnonymousInstanceFunctionExecutionFilter(FunctionExecutionFilter): - param_name = AnonymousInstanceParamName - - class GlobalNameFilter(AbstractUsedNamesFilter): def __init__(self, context, parser_scope): super(GlobalNameFilter, self).__init__(context, parser_scope) From 4a593f9693a0a5694e024c17d9004ebfab682d87 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 6 Aug 2018 11:19:29 +0200 Subject: [PATCH 10/11] Use anonymous instance arguments in a different way --- jedi/evaluate/arguments.py | 5 +-- jedi/evaluate/context/function.py | 3 ++ jedi/evaluate/context/instance.py | 74 +++++++++++++------------------ jedi/evaluate/docstrings.py | 10 +++-- jedi/evaluate/stdlib.py | 7 +-- 5 files changed, 43 insertions(+), 56 deletions(-) diff --git a/jedi/evaluate/arguments.py b/jedi/evaluate/arguments.py index 075ce50a..d0a06224 100644 --- a/jedi/evaluate/arguments.py +++ b/jedi/evaluate/arguments.py @@ -64,7 +64,7 @@ class AbstractArguments(object): try_iter_content(types) def get_calling_nodes(self): - raise NotImplementedError + return [] def unpack(self, funcdef=None): raise NotImplementedError @@ -218,9 +218,6 @@ class ValuesArguments(AbstractArguments): for values in self._values_list: yield None, LazyKnownContexts(values) - def get_calling_nodes(self): - return [] - def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self._values_list) diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index c982f055..cfd10ab4 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -94,6 +94,9 @@ class AbstractFunction(TreeContext): else: return function_execution.get_return_values() + def py__name__(self): + return self.name.string_name + class FunctionContext(use_metaclass(CachedMetaClass, AbstractFunction)): """ diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index dc2c0f52..b5fa0970 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -1,7 +1,5 @@ from abc import abstractproperty -from parso.tree import search_ancestor - from jedi import debug from jedi import settings from jedi.evaluate import compiled @@ -13,36 +11,32 @@ from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction -from jedi.evaluate.filters import FunctionExecutionFilter, ParamName from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope -class _AnonymousInstanceParamName(ParamName): +class InstanceExecutedParam(object): + def __init__(self, instance): + self._instance = instance + def infer(self): - param_node = search_ancestor(self.tree_name, 'param') - # TODO I think this should not belong here. It's not even really true, - # because classmethod and other descriptors can change it. - if param_node.position_index == 0: - # This is a speed optimization, to return the self param (because - # it's known). This only affects anonymous instances. - return ContextSet(self.parent_context.instance) - else: - return self.get_param().infer() + return ContextSet(self._instance) -class _AnonymousInstanceFunctionExecutionFilter(FunctionExecutionFilter): - param_name = _AnonymousInstanceParamName +class AnonymousInstanceArguments(AnonymousArguments): + def __init__(self, instance): + self._instance = instance - -class AnonymousInstanceFunctionExecution(FunctionExecutionContext): - function_execution_filter = _AnonymousInstanceFunctionExecutionFilter - - def __init__(self, instance, *args, **kwargs): - self.instance = instance - super(AnonymousInstanceFunctionExecution, self).__init__( - instance.evaluator, *args, **kwargs) + def get_executed_params(self, execution_context): + from jedi.evaluate.dynamic import search_params + executed_params = list(search_params( + execution_context.evaluator, + execution_context, + execution_context.tree_node + )) + executed_params[0] = InstanceExecutedParam(self._instance) + return executed_params class AbstractInstanceContext(Context): @@ -260,15 +254,7 @@ class AnonymousInstance(TreeInstance): evaluator, parent_context, class_context, - var_args=AnonymousArguments(), - ) - - def _create_init_execution(self, class_context, bound_method): - return AnonymousInstanceFunctionExecution( - self, - class_context.parent_context, - bound_method, - self.var_args + var_args=AnonymousInstanceArguments(self), ) @@ -335,16 +321,14 @@ class BoundMethod(AbstractFunction): def get_function_execution(self, arguments=None): if arguments is None: - arguments = AnonymousArguments() - return AnonymousInstanceFunctionExecution( - self._instance, self.parent_context, self, arguments) - else: - return FunctionExecutionContext( - self.evaluator, - self.parent_context, - self, - InstanceArguments(self._instance, arguments) - ) + arguments = AnonymousInstanceArguments(self._instance) + + return FunctionExecutionContext( + self.evaluator, + self.parent_context, + self, + InstanceArguments(self._instance, arguments) + ) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self._function) @@ -481,3 +465,9 @@ class InstanceArguments(AbstractArguments): def get_calling_nodes(self): return self._var_args.get_calling_nodes() + + def get_executed_params(self, execution_context): + if isinstance(self._var_args, AnonymousInstanceArguments): + return self._var_args.get_executed_params(execution_context) + + return super(InstanceArguments, self).get_executed_params(execution_context) diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index 6fac8b9f..aaef8ea8 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -266,7 +266,8 @@ def _execute_array_values(evaluator, array): @evaluator_method_cache() def infer_param(execution_context, param): - from jedi.evaluate.context.instance import AnonymousInstanceFunctionExecution + from jedi.evaluate.context.instance import InstanceArguments + from jedi.evaluate.context import FunctionExecutionContext def eval_docstring(docstring): return ContextSet.from_iterable( @@ -280,9 +281,10 @@ def infer_param(execution_context, param): return NO_CONTEXTS types = eval_docstring(execution_context.py__doc__()) - if isinstance(execution_context, AnonymousInstanceFunctionExecution) and \ - execution_context.function_context.name.string_name == '__init__': - class_context = execution_context.instance.class_context + if isinstance(execution_context, FunctionExecutionContext) \ + and isinstance(execution_context.var_args, InstanceArguments) \ + and execution_context.function_context.py__name__() == '__init__': + class_context = execution_context.var_args.instance.class_context types |= eval_docstring(class_context.py__doc__()) return types diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index 93977189..3ca38322 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -19,8 +19,7 @@ from jedi.evaluate.arguments import ValuesArguments from jedi.evaluate import analysis from jedi.evaluate import compiled from jedi.evaluate.context.instance import \ - AbstractInstanceContext, CompiledInstance, BoundMethod, \ - AnonymousInstanceFunctionExecution, InstanceArguments + AbstractInstanceContext, CompiledInstance, BoundMethod, InstanceArguments from jedi.evaluate.base_context import ContextualizedNode, \ NO_CONTEXTS, ContextSet from jedi.evaluate.context import ClassContext, ModuleContext, FunctionExecutionContext @@ -192,10 +191,6 @@ def builtins_super(evaluator, types, objects, context): su = context.var_args.instance.py__class__().py__bases__() return su[0].infer().execute_evaluated() - if isinstance(context, AnonymousInstanceFunctionExecution): - su = context.instance.py__class__().py__bases__() - return su[0].infer().execute_evaluated() - return NO_CONTEXTS From 8fc2add242e9367691bc8fa78f786613dd361835 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 6 Aug 2018 12:49:31 +0200 Subject: [PATCH 11/11] FunctionExecutionContext should use the parent if possible --- jedi/evaluate/context/instance.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index b5fa0970..8fcd405d 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -323,12 +323,16 @@ class BoundMethod(AbstractFunction): if arguments is None: arguments = AnonymousInstanceArguments(self._instance) - return FunctionExecutionContext( - self.evaluator, - self.parent_context, - self, - InstanceArguments(self._instance, arguments) - ) + arguments = InstanceArguments(self._instance, arguments) + + if isinstance(self._function, compiled.CompiledObject): + # This is kind of weird, because it's coming from a compiled object + # and we're not sure if we want that in the future. + return FunctionExecutionContext( + self.evaluator, self.parent_context, self, arguments + ) + + return self._function.get_function_execution(arguments) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self._function)