From 9e7879d43f03025872bc4c06ac6f39f46bf16e3e Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 7 Sep 2018 00:46:54 +0200 Subject: [PATCH] Move py__mro__ to a separate function --- jedi/evaluate/context/instance.py | 7 +-- jedi/evaluate/context/klass.py | 87 +++++++++++++++++-------------- jedi/evaluate/context/typing.py | 3 -- jedi/plugins/stdlib.py | 3 +- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index faac655d..a2a6aca1 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -12,7 +12,8 @@ from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments, \ ValuesArguments from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction, OverloadedFunctionContext -from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter +from jedi.evaluate.context.klass import ClassContext, apply_py__get__, \ + py__mro__, ClassFilter from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope @@ -113,7 +114,7 @@ class AbstractInstanceContext(Context): def get_filters(self, search_global=None, until_position=None, origin_scope=None, include_self_names=True): if include_self_names: - for cls in self.class_context.py__mro__(): + for cls in py__mro__(self.class_context): if not isinstance(cls, compiled.CompiledObject) \ or cls.tree_node is not None: # In this case we're excluding compiled objects that are @@ -121,7 +122,7 @@ class AbstractInstanceContext(Context): # compiled objects to search for self variables. yield SelfAttributeFilter(self.evaluator, self, cls, origin_scope) - for cls in self.class_context.py__mro__(): + for cls in py__mro__(self.class_context): if isinstance(cls, compiled.CompiledObject): yield CompiledInstanceClassFilter(self.evaluator, self, cls) else: diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index cf3ed1de..3ff8982e 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -21,7 +21,6 @@ py__call__(arguments: Array) On callable objects, returns types. py__bool__() Returns True/False/None; None means that there's no certainty. py__bases__() Returns a list of base classes. -py__mro__() Returns a list of classes (the mro). py__iter__() Returns a generator of a set of types. py__class__() Returns the class of an instance. py__simple_getitem__(index: int/str) Returns a a set of types of the index. @@ -60,6 +59,51 @@ def apply_py__get__(context, base_context): yield descriptor_context +@evaluator_method_cache(default=()) +def py__mro__(context): + try: + method = context.py__mro__ + except AttributeError: + pass + else: + # Currently only used for compiled objects. + return method() + + def add(cls): + if cls not in mro: + mro.append(cls) + + mro = [context] + # TODO Do a proper mro resolution. Currently we are just listing + # classes. However, it's a complicated algorithm. + for lazy_cls in context.py__bases__(): + # TODO there's multiple different mro paths possible if this yields + # multiple possibilities. Could be changed to be more correct. + for cls in lazy_cls.infer(): + # TODO detect for TypeError: duplicate base class str, + # e.g. `class X(str, str): pass` + try: + mro_method = py__mro__(cls) + except AttributeError: + # TODO add a TypeError like: + """ + >>> class Y(lambda: test): pass + Traceback (most recent call last): + File "", line 1, in + TypeError: function() argument 1 must be code, not str + >>> class Y(1): pass + Traceback (most recent call last): + File "", line 1, in + TypeError: int() takes at most 2 arguments (3 given) + """ + debug.warning('Super class of %s is not a class: %s', context, cls) + else: + add(cls) + for cls_new in mro_method(): + add(cls_new) + return tuple(mro) + + class ClassName(TreeNameDefinition): def __init__(self, parent_context, tree_name, name_context, apply_decorators): super(ClassName, self).__init__(parent_context, tree_name) @@ -122,42 +166,6 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): """ api_type = u'class' - @evaluator_method_cache(default=()) - def py__mro__(self): - def add(cls): - if cls not in mro: - mro.append(cls) - - mro = [self] - # TODO Do a proper mro resolution. Currently we are just listing - # classes. However, it's a complicated algorithm. - for lazy_cls in self.py__bases__(): - # TODO there's multiple different mro paths possible if this yields - # multiple possibilities. Could be changed to be more correct. - for cls in lazy_cls.infer(): - # TODO detect for TypeError: duplicate base class str, - # e.g. `class X(str, str): pass` - try: - mro_method = cls.py__mro__ - except AttributeError: - # TODO add a TypeError like: - """ - >>> class Y(lambda: test): pass - Traceback (most recent call last): - File "", line 1, in - TypeError: function() argument 1 must be code, not str - >>> class Y(1): pass - Traceback (most recent call last): - File "", line 1, in - TypeError: int() takes at most 2 arguments (3 given) - """ - debug.warning('Super class of %s is not a class: %s', self, cls) - else: - add(cls) - for cls_new in mro_method(): - add(cls_new) - return tuple(mro) - @evaluator_method_cache(default=()) def py__bases__(self): arglist = self.tree_node.get_super_arglist() @@ -185,7 +193,7 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): origin_scope=origin_scope ) else: - for cls in self.py__mro__(): + for cls in py__mro__(self): if isinstance(cls, compiled.CompiledObject): for filter in cls.get_filters(is_instance=is_instance): yield filter @@ -226,9 +234,8 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): def py__getitem__(self, index_context_set, contextualized_node): from jedi.evaluate.context.typing import TypingClassMixin, AnnotatedClass - for cls in self.py__mro__(): + for cls in py__mro__(self): if isinstance(cls, TypingClassMixin): - #print('ha', self, list(self.py__mro__())) # TODO get the right classes. return ContextSet.from_iterable( AnnotatedClass( diff --git a/jedi/evaluate/context/typing.py b/jedi/evaluate/context/typing.py index 1a5e8cfa..fff5bb3a 100644 --- a/jedi/evaluate/context/typing.py +++ b/jedi/evaluate/context/typing.py @@ -185,9 +185,6 @@ class TypingContext(_BaseTypingContext): class TypingClassMixin(object): - def py__mro__(self): - return [self, builtin_from_name(self.evaluator, u'object')] - def py__bases__(self,): return [LazyKnownContext(builtin_from_name(self.evaluator, u'object'))] diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 3d180052..4f8173aa 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -24,6 +24,7 @@ from jedi.evaluate.base_context import ContextualizedNode, \ NO_CONTEXTS, ContextSet, ContextWrapper from jedi.evaluate.context import ClassContext, ModuleContext, \ FunctionExecutionContext +from jedi.evaluate.context.klass import py__mro__ from jedi.evaluate.context import iterable from jedi.evaluate.lazy_context import LazyTreeContext from jedi.evaluate.syntax_tree import is_string @@ -229,7 +230,7 @@ def builtins_isinstance(objects, types, arguments, evaluator): for o in objects: cls = o.py__class__() try: - mro_func = cls.py__mro__ + mro_func = py__mro__(cls) except AttributeError: # This is temporary. Everything should have a class attribute in # Python?! Maybe we'll leave it here, because some numpy objects or