def infer_attribute(self, context=None): """infer an Attribute node by using getattr on the associated object""" for owner in self.expr.infer(context): if owner is util.Uninferable: yield owner continue if context and context.boundnode: # This handles the situation where the attribute is accessed through a subclass # of a base class and the attribute is defined at the base class's level, # by taking in consideration a redefinition in the subclass. if (isinstance(owner, bases.Instance) and isinstance(context.boundnode, bases.Instance)): try: if helpers.is_subtype(helpers.object_type(context.boundnode), helpers.object_type(owner)): owner = context.boundnode except exceptions._NonDeducibleTypeHierarchy: # Can't determine anything useful. pass try: context.boundnode = owner yield from owner.igetattr(self.attrname, context) context.boundnode = None except (exceptions.AttributeInferenceError, exceptions.InferenceError): context.boundnode = None except AttributeError: # XXX method / function context.boundnode = None # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. return dict(node=self, context=context)
def infer_attribute(self, context=None): """infer an Attribute node by using getattr on the associated object""" for owner in self.expr.infer(context): if owner is util.Uninferable: yield owner continue if context and context.boundnode: # This handles the situation where the attribute is accessed through a subclass # of a base class and the attribute is defined at the base class's level, # by taking in consideration a redefinition in the subclass. if (isinstance(owner, bases.Instance) and isinstance(context.boundnode, bases.Instance)): try: if helpers.is_subtype(helpers.object_type(context.boundnode), helpers.object_type(owner)): owner = context.boundnode except exceptions._NonDeducibleTypeHierarchy: # Can't determine anything useful. pass try: context.boundnode = owner for obj in owner.igetattr(self.attrname, context): yield obj context.boundnode = None except (exceptions.AttributeInferenceError, exceptions.InferenceError): context.boundnode = None except AttributeError: # XXX method / function context.boundnode = None # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. raise StopIteration(dict(node=self, context=context))
def test_object_type_metaclasses(self): module = builder.parse(''' import abc class Meta(metaclass=abc.ABCMeta): pass meta_instance = Meta() ''') meta_type = helpers.object_type(module['Meta']) self.assert_classes_equal(meta_type, module['Meta'].metaclass()) meta_instance = next(module['meta_instance'].infer()) instance_type = helpers.object_type(meta_instance) self.assert_classes_equal(instance_type, module['Meta'])
def test_object_type_metaclasses(self) -> None: module = builder.parse(""" import abc class Meta(metaclass=abc.ABCMeta): pass meta_instance = Meta() """) meta_type = helpers.object_type(module["Meta"]) self.assert_classes_equal(meta_type, module["Meta"].metaclass()) meta_instance = next(module["meta_instance"].infer()) instance_type = helpers.object_type(meta_instance) self.assert_classes_equal(instance_type, module["Meta"])
def query_attribute(self, context=None): """query an Attribute node by using getattr on the associated object""" res = [] for owner in self.expr.query(context): if owner is util.Uninferable: assert False if owner is util.Unqueryable: continue if owner.query_end: res.extend([owner, util.Unqueryable]) continue if context and context.boundnode: # This handles the situation where the attribute is accessed through a subclass # of a base class and the attribute is defined at the base class's level, # by taking in consideration a redefinition in the subclass. if isinstance(owner, bases.Instance) and isinstance( context.boundnode, bases.Instance): try: if helpers.is_subtype( helpers.object_type(context.boundnode), helpers.object_type(owner), ): owner = context.boundnode except exceptions._NonDeducibleTypeHierarchy: # Can't determine anything useful. pass elif not context: context = contextmod.InferenceContext() try: context.boundnode = owner res.extend(owner.query_attr(self.attrname, context)) except ( exceptions.AttributeInferenceError, exceptions.InferenceError, AttributeError, ): import traceback traceback.print_exc() pass finally: context.boundnode = None if len(res) == 0: return [util.Unqueryable] return res
def test_object_type_classes_and_functions(self): ast_nodes = builder.extract_node( """ def generator(): yield class A(object): def test(self): self #@ @classmethod def cls_method(cls): pass @staticmethod def static_method(): pass A #@ A() #@ A.test #@ A().test #@ A.cls_method #@ A().cls_method #@ A.static_method #@ A().static_method #@ generator() #@ """ ) from_self = helpers.object_type(ast_nodes[0]) cls = next(ast_nodes[1].infer()) self.assert_classes_equal(from_self, cls) cls_type = helpers.object_type(ast_nodes[1]) self.assert_classes_equal(cls_type, self._extract("type")) instance_type = helpers.object_type(ast_nodes[2]) cls = next(ast_nodes[2].infer())._proxied self.assert_classes_equal(instance_type, cls) expected_method_types = [ (ast_nodes[3], "function"), (ast_nodes[4], "method"), (ast_nodes[5], "method"), (ast_nodes[6], "method"), (ast_nodes[7], "function"), (ast_nodes[8], "function"), (ast_nodes[9], "generator"), ] for node, expected in expected_method_types: node_type = helpers.object_type(node) expected_type = self._build_custom_builtin(expected) self.assert_classes_equal(node_type, expected_type)
def test_is_subtype(self) -> None: ast_nodes = builder.extract_node(""" class int_subclass(int): pass class A(object): pass #@ class B(A): pass #@ class C(A): pass #@ int_subclass() #@ """) assert isinstance(ast_nodes, list) cls_a = ast_nodes[0] cls_b = ast_nodes[1] cls_c = ast_nodes[2] int_subclass = ast_nodes[3] int_subclass = helpers.object_type(next(int_subclass.infer())) base_int = self._extract("int") self.assertTrue(helpers.is_subtype(int_subclass, base_int)) self.assertTrue(helpers.is_supertype(base_int, int_subclass)) self.assertTrue(helpers.is_supertype(cls_a, cls_b)) self.assertTrue(helpers.is_supertype(cls_a, cls_c)) self.assertTrue(helpers.is_subtype(cls_b, cls_a)) self.assertTrue(helpers.is_subtype(cls_c, cls_a)) self.assertFalse(helpers.is_subtype(cls_a, cls_b)) self.assertFalse(helpers.is_subtype(cls_a, cls_b))
def test_object_type_classes_and_functions(self): ast_nodes = builder.extract_node(''' def generator(): yield class A(object): def test(self): self #@ @classmethod def cls_method(cls): pass @staticmethod def static_method(): pass A #@ A() #@ A.test #@ A().test #@ A.cls_method #@ A().cls_method #@ A.static_method #@ A().static_method #@ generator() #@ ''') from_self = helpers.object_type(ast_nodes[0]) cls = next(ast_nodes[1].infer()) self.assert_classes_equal(from_self, cls) cls_type = helpers.object_type(ast_nodes[1]) self.assert_classes_equal(cls_type, self._extract('type')) instance_type = helpers.object_type(ast_nodes[2]) cls = next(ast_nodes[2].infer())._proxied self.assert_classes_equal(instance_type, cls) expected_method_types = [ (ast_nodes[3], 'function'), (ast_nodes[4], 'method'), (ast_nodes[5], 'method'), (ast_nodes[6], 'method'), (ast_nodes[7], 'function'), (ast_nodes[8], 'function'), (ast_nodes[9], 'generator'), ] for node, expected in expected_method_types: node_type = helpers.object_type(node) expected_type = self._build_custom_builtin(expected) self.assert_classes_equal(node_type, expected_type)
def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): """Infer a binary operation between a left operand and a right operand This is used by both normal binary operations and augmented binary operations, the only difference is the flow factory used. """ context, reverse_context = _get_binop_contexts(context, left, right) left_type = helpers.object_type(left) right_type = helpers.object_type(right) methods = flow_factory(left, left_type, binary_opnode, right, right_type, context, reverse_context) for method in methods: try: results = list(method()) except AttributeError: continue except exceptions.AttributeInferenceError: continue except exceptions.InferenceError: yield util.Uninferable return else: if any(result is util.Uninferable for result in results): yield util.Uninferable return # TODO(cpopa): since the inference engine might return # more values than are actually possible, we decide # to return util.Uninferable if we have union types. if all(map(_is_not_implemented, results)): continue not_implemented = sum(1 for result in results if _is_not_implemented(result)) if not_implemented and not_implemented != len(results): # Can't decide yet what this is, not yet though. yield util.Uninferable return for result in results: yield result return # TODO(cpopa): yield a BadBinaryOperationMessage here, # since the operation is not supported yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
def test_object_type_classes_and_functions(self): ast_nodes = test_utils.extract_node(''' def generator(): yield class A(object): def test(self): self #@ @classmethod def cls_method(cls): pass @staticmethod def static_method(): pass A #@ A() #@ A.test #@ A().test #@ A.cls_method #@ A().cls_method #@ A.static_method #@ A().static_method #@ generator() #@ ''') from_self = helpers.object_type(ast_nodes[0]) cls = next(ast_nodes[1].infer()) self.assert_classes_equal(from_self, cls) cls_type = helpers.object_type(ast_nodes[1]) self.assert_classes_equal(cls_type, self._extract('type')) instance_type = helpers.object_type(ast_nodes[2]) cls = next(ast_nodes[2].infer())._proxied self.assert_classes_equal(instance_type, cls) expected_method_types = [ (ast_nodes[3], 'instancemethod' if six.PY2 else 'function'), (ast_nodes[4], 'instancemethod' if six.PY2 else 'method'), (ast_nodes[5], 'instancemethod' if six.PY2 else 'method'), (ast_nodes[6], 'instancemethod' if six.PY2 else 'method'), (ast_nodes[7], 'function'), (ast_nodes[8], 'function'), (ast_nodes[9], 'generator'), ] for node, expected in expected_method_types: node_type = helpers.object_type(node) expected_type = self._build_custom_builtin(expected) self.assert_classes_equal(node_type, expected_type)
def test_inference_errors(self): node = builder.extract_node( """ from unknown import Unknown u = Unknown #@ """ ) self.assertEqual(helpers.object_type(node), util.Uninferable)
def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): """Infer a binary operation between a left operand and a right operand This is used by both normal binary operations and augmented binary operations, the only difference is the flow factory used. """ context, reverse_context = _get_binop_contexts(context, left, right) left_type = helpers.object_type(left) right_type = helpers.object_type(right) methods = flow_factory( left, left_type, binary_opnode, right, right_type, context, reverse_context ) for method in methods: try: results = list(method()) except AttributeError: continue except exceptions.AttributeInferenceError: import traceback traceback.print_exc() continue except exceptions.InferenceError: yield util.Uninferable return else: if any(result is util.Uninferable for result in results): yield util.Uninferable return if all(map(_is_not_implemented, results)): continue not_implemented = sum( 1 for result in results if _is_not_implemented(result) ) if not_implemented and not_implemented != len(results): # Can't infer yet what this is. yield util.Uninferable return yield from results return # The operation doesn't seem to be supported so let the caller know about it yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
def test_object_type_too_many_types(self): node = builder.extract_node(''' from unknown import Unknown def test(x): if x: return lambda: None else: return 1 test(Unknown) #@ ''') self.assertEqual(helpers.object_type(node), util.Uninferable)
def test_object_type_too_many_types(self) -> None: node = builder.extract_node(""" from unknown import Unknown def test(x): if x: return lambda: None else: return 1 test(Unknown) #@ """) self.assertEqual(helpers.object_type(node), util.Uninferable)
def test_object_type_too_many_types(self): node = test_utils.extract_node(''' from unknown import Unknown def test(x): if x: return lambda: None else: return 1 test(Unknown) #@ ''') self.assertEqual(helpers.object_type(node), util.Uninferable)
def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): """Infer a binary operation between a left operand and a right operand This is used by both normal binary operations and augmented binary operations, the only difference is the flow factory used. """ context, reverse_context = _get_binop_contexts(context, left, right) left_type = helpers.object_type(left) right_type = helpers.object_type(right) methods = flow_factory(left, left_type, binary_opnode, right, right_type, context, reverse_context) for method in methods: try: results = list(method()) except AttributeError: continue except exceptions.AttributeInferenceError: continue except exceptions.InferenceError: yield util.Uninferable return else: if any(result is util.Uninferable for result in results): yield util.Uninferable return if all(map(_is_not_implemented, results)): continue not_implemented = sum(1 for result in results if _is_not_implemented(result)) if not_implemented and not_implemented != len(results): # Can't infer yet what this is. yield util.Uninferable return for result in results: yield result return # The operation doesn't seem to be supported so let the caller know about it yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
def infer_attribute(self, context=None): """infer an Attribute node by using getattr on the associated object""" for owner in self.expr.infer(context): if owner is util.Uninferable: yield owner continue if context and context.boundnode: # This handles the situation where the attribute is accessed through a subclass # of a base class and the attribute is defined at the base class's level, # by taking in consideration a redefinition in the subclass. if isinstance(owner, bases.Instance) and isinstance( context.boundnode, bases.Instance ): try: if helpers.is_subtype( helpers.object_type(context.boundnode), helpers.object_type(owner), ): owner = context.boundnode except _NonDeducibleTypeHierarchy: # Can't determine anything useful. pass elif not context: context = contextmod.InferenceContext() old_boundnode = context.boundnode try: context.boundnode = owner yield from owner.igetattr(self.attrname, context) except ( AttributeInferenceError, InferenceError, AttributeError, ): pass finally: context.boundnode = old_boundnode return dict(node=self, context=context)
def test_object_type_most_derived(self): node = builder.extract_node(''' class A(type): def __new__(*args, **kwargs): return type.__new__(*args, **kwargs) class B(object): pass class C(object, metaclass=A): pass # The most derived metaclass of D is A rather than type. class D(B , C): #@ pass ''') metaclass = node.metaclass() self.assertEqual(metaclass.name, 'A') obj_type = helpers.object_type(node) self.assertEqual(metaclass, obj_type)
def test_object_type_most_derived(self) -> None: node = builder.extract_node(""" class A(type): def __new__(*args, **kwargs): return type.__new__(*args, **kwargs) class B(object): pass class C(object, metaclass=A): pass # The most derived metaclass of D is A rather than type. class D(B , C): #@ pass """) assert isinstance(node, nodes.NodeNG) metaclass = node.metaclass() self.assertEqual(metaclass.name, "A") obj_type = helpers.object_type(node) self.assertEqual(metaclass, obj_type)
def test_object_type(self): pairs = [ ('1', self._extract('int')), ('[]', self._extract('list')), ('{1, 2, 3}', self._extract('set')), ('{1:2, 4:3}', self._extract('dict')), ('type', self._extract('type')), ('object', self._extract('type')), ('object()', self._extract('object')), ('lambda: None', self._build_custom_builtin('function')), ('len', self._build_custom_builtin('builtin_function_or_method')), ('None', self._build_custom_builtin('NoneType')), ('import sys\nsys#@', self._build_custom_builtin('module')), ] for code, expected in pairs: node = builder.extract_node(code) objtype = helpers.object_type(node) self.assert_classes_equal(objtype, expected)
def test_object_type(self) -> None: pairs = [ ("1", self._extract("int")), ("[]", self._extract("list")), ("{1, 2, 3}", self._extract("set")), ("{1:2, 4:3}", self._extract("dict")), ("type", self._extract("type")), ("object", self._extract("type")), ("object()", self._extract("object")), ("lambda: None", self._build_custom_builtin("function")), ("len", self._build_custom_builtin("builtin_function_or_method")), ("None", self._build_custom_builtin("NoneType")), ("import sys\nsys#@", self._build_custom_builtin("module")), ] for code, expected in pairs: node = builder.extract_node(code) objtype = helpers.object_type(node) self.assert_classes_equal(objtype, expected)
def test_is_subtype(self): ast_nodes = builder.extract_node(''' class int_subclass(int): pass class A(object): pass #@ class B(A): pass #@ class C(A): pass #@ int_subclass() #@ ''') cls_a = ast_nodes[0] cls_b = ast_nodes[1] cls_c = ast_nodes[2] int_subclass = ast_nodes[3] int_subclass = helpers.object_type(next(int_subclass.infer())) base_int = self._extract('int') self.assertTrue(helpers.is_subtype(int_subclass, base_int)) self.assertTrue(helpers.is_supertype(base_int, int_subclass)) self.assertTrue(helpers.is_supertype(cls_a, cls_b)) self.assertTrue(helpers.is_supertype(cls_a, cls_c)) self.assertTrue(helpers.is_subtype(cls_b, cls_a)) self.assertTrue(helpers.is_subtype(cls_c, cls_a)) self.assertFalse(helpers.is_subtype(cls_a, cls_b)) self.assertFalse(helpers.is_subtype(cls_a, cls_b))
def py__class__(self): from astroid import helpers return helpers.object_type(self._instance)
def test_inference_errors(self): node = test_utils.extract_node(''' from unknown import Unknown u = Unknown #@ ''') self.assertEqual(helpers.object_type(node), util.Uninferable)
def infer_type(node, context=None): """Understand the one-argument form of *type*.""" if len(node.args) != 1: raise UseInferenceDefault return helpers.object_type(node.args[0], context)
def attr___class__(self): # pylint: disable=import-outside-toplevel; circular import from astroid import helpers return helpers.object_type(self._instance)
def test_inference_errors_2(self) -> None: node = builder.extract_node("type(float.__new__.__code__)") self.assertIs(helpers.object_type(node), util.Uninferable)
def test_inference_errors(self): node = builder.extract_node(''' from unknown import Unknown u = Unknown #@ ''') self.assertEqual(helpers.object_type(node), util.Uninferable)
def attr___class__(self): from astroid import helpers return helpers.object_type(self._instance)