def type_is_subtype_instance_of_generic_type_when_type_is_not_direct_instantiation(self): generic_type = types.generic_structural_type("box", ["T"], lambda T: [ types.attr("value", T) ]) class_type = types.class_type("boxed_int", [types.attr("value", "int")]) assert types.is_instantiated_sub_type(generic_type, class_type) assert types.is_instantiated_sub_type(generic_type, generic_type(types.int_type)) assert not types.is_instantiated_sub_type(generic_type, types.class_type("empty"))
def object_can_be_called_if_it_has_call_magic_method_that_returns_callable(): second_cls = types.class_type("Second", [ types.attr("__call__", types.func([types.str_type], types.int_type)), ]) first_cls = types.class_type("First", [ types.attr("__call__", second_cls), ]) type_bindings = {"f": first_cls} assert_equal(types.int_type, infer(nodes.call(nodes.ref("f"), [nodes.str_literal("")]), type_bindings=type_bindings))
def instantiated_types_are_not_equal_if_they_have_the_same_generic_type_but_different_substitutions(self): first_class_type = types.class_type("Blah") second_class_type = types.class_type("Blah") generic_type = types.generic_class("List", ["T"]) first_instantiated_type = generic_type.instantiate([first_class_type]) second_instantiated_type = generic_type.instantiate([second_class_type]) assert_not_equal(first_instantiated_type, second_instantiated_type)
def invariant_type_parameter_cannot_have_different_values_in_same_is_sub_type_relation(self): invariant_type_param = types.invariant("T") first_class_type = types.class_type("User") second_class_type = types.class_type("Role") generic_class = types.generic_class("Pair", [invariant_type_param, invariant_type_param]) assert not types.is_sub_type( # TODO: need a reliable way of getting the underlying type (but as an instantiated type) generic_class(invariant_type_param, invariant_type_param), generic_class(first_class_type, second_class_type), unify=[invariant_type_param] )
def contravariant_type_parameter_is_substituted_with_common_sub_type_of_actual_type_params(self): contravariant_type_param = types.contravariant("T") first_class_type = types.class_type("User") second_class_type = types.class_type("Role") generic_class = types.generic_class("Pair", [contravariant_type_param, contravariant_type_param]) type_map = types.is_sub_type( # TODO: need a reliable way of getting the underlying type (but as an instantiated type) generic_class(contravariant_type_param, contravariant_type_param), generic_class(first_class_type, second_class_type), unify=[contravariant_type_param] ) assert_equal(types.bottom_type, type_map[contravariant_type_param])
def type_of_add_method_argument_allows_super_type(): cls = types.class_type("Addable", {}) cls.attrs.add("__add__", types.func([types.object_type], cls)) type_bindings = {"x": cls, "y": cls} addition = nodes.add(nodes.ref("x"), nodes.ref("y")) assert_equal(cls, infer(addition, type_bindings=type_bindings))
def return_type_of_add_can_differ_from_original_type(): cls = types.class_type("Addable", {}) cls.attrs.add("__add__", types.func([types.object_type], types.object_type)) type_bindings = {"x": cls, "y": cls} addition = nodes.add(nodes.ref("x"), nodes.ref("y")) assert_equal(types.object_type, infer(addition, type_bindings=type_bindings))
def can_infer_type_of_subscript_using_getitem(): cls = types.class_type("Blah", [ types.attr("__getitem__", types.func([types.int_type], types.str_type)), ]) type_bindings = {"x": cls} node = nodes.subscript(nodes.ref("x"), nodes.int_literal(4)) assert_equal(types.str_type, infer(node, type_bindings=type_bindings))
def class_type_is_not_subtype_of_structural_type_if_it_is_missing_attrs(self): cls = types.class_type("Person") structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert not types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def error_if_base_class_is_not_object(): type_bindings = {"Person": types.meta_type(types.class_type("Person"))} node = nodes.class_("User", [], base_classes=[nodes.ref("Person")]) try: _infer_class_type(node, [], type_bindings=type_bindings) assert False, "Expected error" except errors.UnsupportedError as error: assert_equal("base classes other than 'object' are not supported", str(error))
def class_type_is_not_subtype_of_structural_type_if_attr_is_strict_supertype_of_attr_on_structural_type(self): cls = types.class_type("Person", [ types.attr("name", types.object_type), ]) structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert not types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def context_manager_of_with_statement_must_have_exit_method(): cls = types.class_type("Manager", [types.attr("__enter__", enter_method())]) context_manager_node = nodes.ref("x") node = nodes.with_(context_manager_node, None, []) try: update_context(node, type_bindings={"x": cls}) assert False, "Expected error" except errors.NoSuchAttributeError as error: assert_equal(nodes.attr(context_manager_node, "__exit__"), error.node)
def iter_method_must_return_iterator(): cls = types.class_type("Blah") cls.attrs.add("__iter__", types.func([], types.iterable(types.str_type))) ref_node = nodes.ref("xs") node = nodes.for_(nodes.ref("x"), ref_node, []) try: update_context(node, type_bindings={"x": None, "xs": cls}) assert False, "Expected error" except errors.BadSignatureError as error: assert_equal(ref_node, error.node)
def callee_must_be_function_or_have_call_magic_method(): cls = types.class_type("Blah", {}) type_bindings = {"f": cls} callee_node = nodes.ref("f") try: infer(nodes.call(callee_node, []), type_bindings=type_bindings) assert False, "Expected error" except errors.UnexpectedValueTypeError as error: assert_equal(callee_node, error.node) assert_equal("callable object", error.expected) assert_equal(cls, error.actual)
def call_attribute_must_be_function(): cls = types.class_type("Blah", [types.attr("__call__", types.int_type)]) type_bindings = {"f": cls} callee_node = nodes.ref("f") try: infer(nodes.call(callee_node, []), type_bindings=type_bindings) assert False, "Expected error" except errors.UnexpectedValueTypeError as error: assert_equal(callee_node, ephemeral.root_node(error.node)) assert_equal("callable object", error.expected) assert_equal(types.int_type, error.actual)
def add_method_should_only_accept_one_argument(): cls = types.class_type("NotAddable", {}) cls.attrs.add("__add__", types.func([types.object_type, types.object_type], cls)) type_bindings = {"x": cls, "y": cls} addition = nodes.add(nodes.ref("x"), nodes.ref("y")) try: infer(addition, type_bindings=type_bindings) assert False, "Expected error" except errors.TypeCheckError as error: assert_equal(addition, ephemeral.root_node(error.node))
def iter_method_must_take_no_arguments(): cls = types.class_type("Blah") cls.attrs.add("__iter__", types.func([types.str_type], types.iterable(types.str_type))) ref_node = nodes.ref("xs") node = nodes.for_(nodes.ref("x"), ref_node, []) try: update_context(node, type_bindings={"x": None, "xs": cls}) assert False, "Expected error" except errors.TypeCheckError as error: assert_equal(ref_node, ephemeral.root_node(error.node))
def cannot_reassign_read_only_attribute(): cls = types.class_type("X", [types.attr("y", types.str_type, read_only=True)]) attr_node = nodes.attr(nodes.ref("x"), "y") node = nodes.assign([attr_node], nodes.str_literal("Hello")) type_bindings = {"x": cls} try: update_context(node, type_bindings=type_bindings) assert False, "Expected error" except errors.ReadOnlyAttributeError as error: assert_equal(attr_node, error.node) assert_equal("'X' attribute 'y' is read-only", str(error))
def class_type_is_subtype_of_structural_type_if_it_has_subset_of_attrs(self): # TODO: how to handle sub-typing of mutable attrs cls = types.class_type("Person", [ types.attr("name", types.str_type), types.attr("number_of_hats", types.int_type), ]) structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def assignment_to_attribute_does_not_allow_strict_supertype(): cls = types.class_type("X", [types.attr("y", types.str_type, read_only=True)]) attr_node = nodes.attr(nodes.ref("x"), "y") node = nodes.assign([attr_node], nodes.ref("obj")) type_bindings = {"x": cls, "obj": types.object_type} try: update_context(node, type_bindings=type_bindings) assert False, "Expected error" except errors.UnexpectedTargetTypeError as error: assert_equal(attr_node, error.node) assert_equal(types.object_type, error.value_type) assert_equal(types.str_type, error.target_type)
def for_statement_accepts_iterable_with_iter_method(): cls = types.class_type("Blah") cls.attrs.add("__iter__", types.func([], types.iterator(types.str_type))) node = nodes.for_(nodes.ref("x"), nodes.ref("xs"), []) type_bindings = { "xs": cls, } context = update_context(node, type_bindings=type_bindings) assert_equal(types.str_type, context.lookup_name("x"))
def for_statement_requires_iterable_getitem_method_to_accept_integers(): cls = types.class_type("Blah") cls.attrs.add("__getitem__", types.func([types.str_type], types.str_type)) ref_node = nodes.ref("xs") node = nodes.for_(nodes.ref("x"), ref_node, []) type_bindings = { "xs": cls, } try: update_context(node, type_bindings=type_bindings) assert False, "Expected error" except errors.UnexpectedTargetTypeError as error: assert_equal(ref_node, ephemeral.root_node(error.node)) assert_equal( ephemeral.FormalArg(ephemeral.attr(ref_node, "__getitem__"), 0), ephemeral.underlying_node(error.node) ) assert_equal(types.int_type, error.value_type) assert_equal(types.str_type, error.target_type)
def instantiated_types_are_equal_if_they_have_the_same_substitutions_and_generic_type(self): class_type = types.class_type("Blah") generic_type = types.generic_class("List", "T") first_instantiated_type = generic_type.instantiate([class_type]) second_instantiated_type = generic_type.instantiate([class_type]) assert_equal(first_instantiated_type, second_instantiated_type)
def class_type_with_call_magic_method_is_not_func_type(self): class_type = types.class_type("A", [ types.attr("__call__", types.func([], types.none_type)), ]) assert not types.is_func_type(class_type)
def class_type_is_subtype_of_base_class_of_base_class(self): super_super_type = types.class_type("GrandParent") super_type = types.class_type("Parent", base_classes=[super_super_type]) cls = types.class_type("Blah", base_classes=[super_type]) assert types.is_sub_type(super_super_type, cls) assert not types.is_sub_type(cls, super_super_type)
def class_type_is_subtype_of_object_type(self): cls = types.class_type("Blah") assert types.is_sub_type(types.object_type, cls)
def class_type_is_subtype_of_itself(self): cls = types.class_type("Blah") assert types.is_sub_type(cls, cls)
def object_can_be_called_if_it_has_call_magic_method(): cls = types.class_type("Blah", [ types.attr("__call__", types.func([types.str_type], types.int_type)), ]) type_bindings = {"f": cls} assert_equal(types.int_type, infer(nodes.call(nodes.ref("f"), [nodes.str_literal("")]), type_bindings=type_bindings))
def context_manager_class(enter_type=None, exit_type=None): return types.class_type("Manager", [ types.attr("__enter__", enter_method(enter_type), read_only=True), types.attr("__exit__", exit_method(exit_type), read_only=True), ])
def class_type_is_not_func_type(self): class_type = types.class_type("A") assert not types.is_func_type(class_type)