diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 7c99796c..ef823381 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -19,6 +19,7 @@ CODES = { 'type-error-star': (10, TypeError, None), 'type-error-operation': (11, TypeError, None), 'type-error-not-iterable': (12, TypeError, None), + 'type-error-isinstance': (13, TypeError, None), } diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 88555543..b5b5ecba 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -208,8 +208,8 @@ class CompiledObject(Base): # Get rid of side effects, we won't call custom `__getitem__`s. return - for obj in self.obj: - yield set([CompiledObject(obj)]) + for part in self.obj: + yield set([CompiledObject(part)]) return actual @property diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index e33f2f3e..feef396d 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -12,8 +12,13 @@ from jedi.evaluate.helpers import FakeName from jedi.cache import underscore_memoization -def try_iter_content(types): +def try_iter_content(types, depth=0): """Helper method for static analysis.""" + if depth > 10: + # It's possible that a loop has references on itself (especially with + # CompiledObject). Therefore don't loop infinitely. + return + for typ in types: try: f = typ.py__iter__ @@ -21,7 +26,7 @@ def try_iter_content(types): pass else: for iter_types in f(): - try_iter_content(iter_types) + try_iter_content(iter_types, depth + 1) class Arguments(tree.Base): diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index c658448c..98d5351b 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -19,6 +19,7 @@ from jedi.parser import tree from jedi import debug from jedi.evaluate import precedence from jedi.evaluate import param +from jedi.evaluate import analysis class NotInStdLib(LookupError): @@ -40,9 +41,11 @@ def execute(evaluator, obj, arguments): # for now we just support builtin functions. try: - return _implemented[module_name][obj_name](evaluator, obj, arguments) + func = _implemented[module_name][obj_name] except KeyError: pass + else: + return func(evaluator, obj, arguments) raise NotInStdLib() @@ -186,12 +189,15 @@ def builtins_isinstance(evaluator, objects, types, arguments): for cls_or_tup in types: if cls_or_tup.is_class(): bool_results.add(cls_or_tup in mro) - else: - # TODO Actually we should check for non iterables here (the - # type should be object or tuple of object) + elif str(cls_or_tup.name) == 'tuple' \ + and cls_or_tup.get_parent_scope() == compiled.builtin: # Check for tuples. - classes = iterable.py__iter__types(evaluator, set([cls_or_tup])) + classes = unite(cls_or_tup.py__iter__()) bool_results.add(any(cls in mro for cls in classes)) + else: + _, nodes = list(arguments.unpack())[1] + for node in nodes: + analysis.add(evaluator, 'type-error-isinstance', node) return set(compiled.keyword_from_value(x) for x in bool_results)