diff --git a/jedi/evaluate/arguments.py b/jedi/evaluate/arguments.py index b7a9c8eb..d0a06224 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): @@ -64,17 +64,17 @@ class AbstractArguments(object): try_iter_content(types) def get_calling_nodes(self): - raise NotImplementedError + return [] 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, @@ -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/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 diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index ecb7d6c0..822f6651 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)): """ @@ -250,5 +253,5 @@ class FunctionExecutionContext(TreeContext): origin_scope=origin_scope) @evaluator_method_cache() - def get_params(self): - return self.var_args.get_params(self) + def get_executed_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..8fcd405d 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, \ @@ -8,7 +9,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 @@ -16,27 +16,27 @@ 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 InstanceExecutedParam(object): + def __init__(self, instance): + self._instance = instance + + def infer(self): + return ContextSet(self._instance) -class InstanceFunctionExecution(BaseInstanceFunctionExecution): - def __init__(self, instance, parent_context, function_context, var_args): - var_args = InstanceVarArgs(self, var_args) +class AnonymousInstanceArguments(AnonymousArguments): + def __init__(self, instance): + self._instance = instance - super(InstanceFunctionExecution, self).__init__( - instance, parent_context, function_context, var_args) - - -class AnonymousInstanceFunctionExecution(BaseInstanceFunctionExecution): - function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter - - def __init__(self, instance, parent_context, function_context, var_args): - super(AnonymousInstanceFunctionExecution, self).__init__( - instance, parent_context, function_context, var_args) + 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): @@ -44,7 +44,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) @@ -160,12 +159,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__'): @@ -212,16 +206,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): @@ -253,14 +249,12 @@ class TreeInstance(AbstractInstanceContext): class AnonymousInstance(TreeInstance): - function_execution_cls = AnonymousInstanceFunctionExecution - def __init__(self, evaluator, parent_context, class_context): super(AnonymousInstance, self).__init__( evaluator, parent_context, class_context, - var_args=AnonymousArguments(), + var_args=AnonymousInstanceArguments(self), ) @@ -327,12 +321,21 @@ 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 InstanceFunctionExecution( - self._instance, self.parent_context, self, arguments) + arguments = AnonymousInstanceArguments(self._instance) + + 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) class CompiledBoundMethod(compiled.CompiledObject): @@ -446,15 +449,11 @@ class SelfAttributeFilter(ClassFilter): return names -class InstanceVarArgs(AbstractArguments): - def __init__(self, execution_context, var_args): - self._execution_context = execution_context +class InstanceArguments(AbstractArguments): + def __init__(self, instance, var_args): + 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 @@ -464,9 +463,15 @@ class InstanceVarArgs(AbstractArguments): return self._var_args.trailer def unpack(self, func=None): - yield None, LazyKnownContext(self._execution_context.instance) - for values in self._get_var_args().unpack(func): + yield None, LazyKnownContext(self.instance) + 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() + + 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/context/iterable.py b/jedi/evaluate/context/iterable.py index 9fec7fe9..622f2304 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -631,12 +631,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)]) @@ -652,9 +649,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/context/klass.py b/jedi/evaluate/context/klass.py index 3343ec07..ca17f348 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 @@ -173,11 +173,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/docstrings.py b/jedi/evaluate/docstrings.py index 5df1a045..f2676355 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -267,7 +267,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( @@ -281,9 +282,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/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 fae4f9e0..6f4f0599 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -136,24 +136,11 @@ 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] -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) 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 diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 70a4b115..0477c414 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 5d29ace9..787cd2c4 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -278,7 +278,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: diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index a47efddb..c87c1822 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -19,12 +19,11 @@ 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, \ - AbstractInstanceContext, CompiledInstance, BoundMethod, \ - AnonymousInstanceFunctionExecution +from jedi.evaluate.context.instance import \ + AbstractInstanceContext, CompiledInstance, BoundMethod, 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 @@ -202,10 +201,11 @@ 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)): - su = context.instance.py__class__().py__bases__() - return su[0].infer().execute_evaluated() + 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() + return NO_CONTEXTS