diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 5bc5181c..4aec2dcc 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -421,6 +421,17 @@ true_obj = builtin.get_by_name('True') object_obj = builtin.get_by_name('object') +def keyword_from_value(obj): + if obj is None: + return none_obj + elif obj is False: + return false_obj + elif obj is True: + return true_obj + else: + raise NotImplementedError + + def compiled_objects_cache(func): def wrapper(evaluator, obj, parent=builtin, module=None): # Do a very cheap form of caching here. diff --git a/jedi/evaluate/precedence.py b/jedi/evaluate/precedence.py index 325c186f..05497f47 100644 --- a/jedi/evaluate/precedence.py +++ b/jedi/evaluate/precedence.py @@ -7,8 +7,8 @@ from jedi._compatibility import unicode from jedi.parser import representation as pr from jedi import debug from jedi.common import PushBackIterator -from jedi.evaluate.compiled import (CompiledObject, create, builtin, false_obj, - true_obj, none_obj) +from jedi.evaluate.compiled import (CompiledObject, create, builtin, + keyword_from_value) from jedi.evaluate import analysis # Maps Python syntax to the operator module. @@ -249,7 +249,7 @@ def _factor_calculate(evaluator, operator, right): value = right.py__bool__() if value is None: # Uncertainty. return None - return _keyword_from_value(not value) + return keyword_from_value(not value) return right @@ -277,15 +277,6 @@ def _is_list(obj): return isinstance(obj, iterable.Array) and obj.type == pr.Array.LIST -def _keyword_from_value(obj): - if obj is None: - return none_obj - elif obj is False: - return false_obj - elif obj is True: - return true_obj - - def _element_calculate(evaluator, left, operator, right): from jedi.evaluate import iterable, representation as er l_is_num = _is_number(left) @@ -314,7 +305,7 @@ def _element_calculate(evaluator, left, operator, right): # Possible, because the return is not an option. Just compare. left = left.obj right = right.obj - return [_keyword_from_value(operation(left, right))] + return [keyword_from_value(operation(left, right))] def check(obj): """Checks if a Jedi object is either a float or an int.""" diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index b081a7de..2d79d73e 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -124,6 +124,31 @@ def builtins_reversed(evaluator, obj, params): return [er.Instance(evaluator, obj, objects)] +def builtins_isinstance(evaluator, obj, params): + any_bool = [compiled.false_obj, compiled.true_obj] + obj = _follow_param(evaluator, params, 0) + raw_classes = _follow_param(evaluator, params, 1) + classes = [] + # Check for tuples. + for cls_or_tup in raw_classes: + if isinstance(cls_or_tup, er.Class): + classes.append(cls_or_tup) + else: + classes += iterable.get_iterator_types([cls_or_tup]) + + bool_results = [] + for o in obj: + for cls in classes: + try: + mro_func = o.base.py__mro__ + except AttributeError: + return any_bool + else: + bool_result = cls in mro_func(evaluator) + bool_results.append(compiled.keyword_from_value(bool_result)) + return set(bool_results) + + def collections_namedtuple(evaluator, obj, params): """ Implementation of the namedtuple function. @@ -179,6 +204,7 @@ _implemented = { 'type': builtins_type, 'super': builtins_super, 'reversed': builtins_reversed, + 'isinstance': builtins_isinstance, }, 'copy': { 'copy': _return_first_param, diff --git a/test/completion/flow_analysis.py b/test/completion/flow_analysis.py index 9c3c7bfd..65ae8dd4 100644 --- a/test/completion/flow_analysis.py +++ b/test/completion/flow_analysis.py @@ -155,8 +155,12 @@ a # isinstance # ----------------- +class A(): pass + def isinst(x): - if isinstance(x, int) and x == 1 or x is True: + if isinstance(x, A): + return dict + elif isinstance(x, int) and x == 1 or x is True: return set elif isinstance(x, (float, tuple)): return list @@ -164,8 +168,8 @@ def isinst(x): return tuple return 1 -#? set -isinst(1) +#? dict +isinst(A()) #? set isinst(True) #? list