diff --git a/jedi/evaluate/arguments.py b/jedi/evaluate/arguments.py index eeef882a..da20230a 100644 --- a/jedi/evaluate/arguments.py +++ b/jedi/evaluate/arguments.py @@ -128,11 +128,7 @@ def _parse_argument_clinic(string): allow_kwargs = True -class AbstractArguments(object): - context = None - argument_node = None - trailer = None - +class _AbstractArgumentsMixin(object): def eval_all(self, funcdef=None): """ Evaluates all arguments as a support for static analysis @@ -142,15 +138,21 @@ class AbstractArguments(object): types = lazy_context.infer() try_iter_content(types) - def get_calling_nodes(self): - return [] - def unpack(self, funcdef=None): raise NotImplementedError def get_executed_params_and_issues(self, execution_context): return get_executed_params_and_issues(execution_context, self) + def get_calling_nodes(self): + return [] + + +class AbstractArguments(_AbstractArgumentsMixin): + context = None + argument_node = None + trailer = None + class AnonymousArguments(AbstractArguments): def get_executed_params_and_issues(self, execution_context): @@ -302,6 +304,28 @@ class ValuesArguments(AbstractArguments): return '<%s: %s>' % (self.__class__.__name__, self._values_list) +class TreeArgumentsWrapper(_AbstractArgumentsMixin): + def __init__(self, arguments): + self._wrapped_arguments = arguments + + @property + def argument_node(self): + return self._wrapped_arguments.argument_node + + @property + def trailer(self): + return self._wrapped_arguments.trailer + + def unpack(self, func=None): + raise NotImplementedError + + def get_calling_nodes(self): + return self._wrapped_arguments.get_calling_nodes() + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self._wrapped_arguments) + + def _iterate_star_args(context, array, input_node, funcdef=None): try: iter_ = array.py__iter__ diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 8830545b..612869eb 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -9,7 +9,7 @@ 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, \ - ValuesArguments + ValuesArguments, TreeArgumentsWrapper from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction, OverloadedFunctionContext from jedi.evaluate.context.klass import ClassContext, apply_py__get__, \ @@ -522,32 +522,18 @@ class SelfAttributeFilter(ClassFilter): return names -class InstanceArguments(AbstractArguments): +class InstanceArguments(TreeArgumentsWrapper): def __init__(self, instance, arguments): + super(InstanceArguments, self).__init__(arguments) self.instance = instance - self._arguments = arguments - - @property - def argument_node(self): - return self._arguments.argument_node - - @property - def trailer(self): - return self._arguments.trailer def unpack(self, func=None): yield None, LazyKnownContext(self.instance) - for values in self._arguments.unpack(func): + for values in self._wrapped_arguments.unpack(func): yield values - def get_calling_nodes(self): - return self._arguments.get_calling_nodes() - def get_executed_params_and_issues(self, execution_context): - if isinstance(self._arguments, AnonymousInstanceArguments): - return self._arguments.get_executed_params_and_issues(execution_context) + if isinstance(self._wrapped_arguments, AnonymousInstanceArguments): + return self._wrapped_arguments.get_executed_params_and_issues(execution_context) return super(InstanceArguments, self).get_executed_params_and_issues(execution_context) - - def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self._arguments) diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 16eba511..8cb5cf53 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -15,19 +15,18 @@ from jedi._compatibility import force_unicode from jedi.plugins.base import BasePlugin from jedi import debug from jedi.evaluate.arguments import ValuesArguments, \ - repack_with_argument_clinic, AbstractArguments + repack_with_argument_clinic, AbstractArguments, TreeArgumentsWrapper from jedi.evaluate import analysis from jedi.evaluate import compiled from jedi.evaluate.context.instance import TreeInstance, \ - AbstractInstanceContext, CompiledInstance, BoundMethod, InstanceArguments + AbstractInstanceContext, BoundMethod, InstanceArguments from jedi.evaluate.base_context import ContextualizedNode, \ NO_CONTEXTS, ContextSet, ContextWrapper from jedi.evaluate.context import ClassContext, ModuleContext, \ FunctionExecutionContext -from jedi.plugins import typeshed from jedi.evaluate.context.klass import py__mro__ from jedi.evaluate.context import iterable -from jedi.evaluate.lazy_context import LazyTreeContext +from jedi.evaluate.lazy_context import LazyTreeContext, LazyKnownContext from jedi.evaluate.syntax_tree import is_string # Now this is all part of fake tuples in Jedi. However super doesn't work on @@ -280,6 +279,69 @@ def builtins_isinstance(objects, types, arguments, evaluator): ) +class StaticMethodObject(AbstractObjectOverwrite, ContextWrapper): + def get_object(self): + return self._wrapped_context + + @publish_method('__get__') + def _py__get__(self): + return ContextSet([self._wrapped_context]) + + +@argument_clinic('sequence, /') +def builtins_staticmethod(functions): + return ContextSet(StaticMethodObject(f) for f in functions) + + +class ClassMethodObject(AbstractObjectOverwrite, ContextWrapper): + def __init__(self, class_method_obj, function): + super(ClassMethodObject, self).__init__(class_method_obj) + self._function = function + + def get_object(self): + return self._wrapped_context + + def py__get__(self, obj): + actual, = self._wrapped_context.py__getattribute__('__get__') + klass = obj + if not obj.is_class(): + klass = obj.py__class__() + return ContextSet([ClassMethodGet(actual, klass, self._function)]) + + +class ClassMethodGet(AbstractObjectOverwrite, ContextWrapper): + def __init__(self, get_method, klass, function): + super(ClassMethodGet, self).__init__(get_method) + self._class = klass + self._function = function + + def get_object(self): + return self._wrapped_context + + def py__call__(self, arguments): + return self._function.execute(ClassMethodArguments(self._class, arguments)) + + +class ClassMethodArguments(TreeArgumentsWrapper): + def __init__(self, klass, arguments): + super(ClassMethodArguments, self).__init__(arguments) + self._class = klass + + def unpack(self, func=None): + yield None, LazyKnownContext(self._class) + for values in self._wrapped_arguments.unpack(func): + yield values + + +@argument_clinic('sequence, /', want_obj=True, want_arguments=True) +def builtins_classmethod(functions, obj, arguments): + return ContextSet( + ClassMethodObject(class_method_object, function) + for class_method_object in obj.py__call__(arguments=arguments) + for function in functions + ) + + def collections_namedtuple(obj, arguments): """ Implementation of the namedtuple function. @@ -434,6 +496,8 @@ _implemented = { 'isinstance': builtins_isinstance, 'next': builtins_next, 'iter': builtins_iter, + 'staticmethod': builtins_staticmethod, + 'classmethod': builtins_classmethod, }, 'copy': { 'copy': _return_first_param,