def type_is_instance_of_generic_type_when_type_is_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 generic_type.is_instantiated_type(generic_type(types.int_type)) assert not generic_type.is_instantiated_type(class_type)
def common_super_type_of_types_is_passed_type_that_is_super_type_of_all_other_types(self): first_type = types.structural_type("first", [ types.attr("a", types.int_type), types.attr("b", types.str_type), ]) second_type = types.structural_type("second", [ types.attr("a", types.int_type), ]) assert_equal(second_type, types.common_super_type([first_type, second_type]))
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 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 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 instantiated_generic_structural_type_is_sub_type_of_other_instantiated_generic_structural_type_if_it_has_matching_attributes(self): iterator = types.generic_structural_type("iterator", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], iterator(T))), types.attr("__next__", types.func([], T)), ]) iterable = types.generic_structural_type("iterable", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], iterator(T))), ]) assert types.is_sub_type( iterable(types.int_type), iterator(types.int_type), )
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 can_import_module_using_plain_import_syntax(): node = nodes.Import([nodes.import_alias("message", None)]) context = _update_blank_context(node, { ("message", ): module_type("message", [types.attr("value", types.str_type)]) }) assert_equal(types.str_type, context.lookup_name("message").attrs.type_of("value"))
def recursive_instantiated_generic_structural_type_is_sub_type_of_same_instantiated_generic_structural_type_if_it_has_matching_attributes(self): recursive = types.generic_structural_type("recursive", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], recursive(T))), ]) assert types.is_sub_type( recursive(types.int_type), recursive(types.int_type), )
def can_import_value_from_relative_module_using_import_from_syntax(): node = nodes.import_from([".", "message"], [nodes.import_alias("value", None)]) context = _update_blank_context(node, { (".", "message"): module_type("message", [types.attr("value", types.str_type)]) }) assert_equal(types.str_type, context.lookup_name("value")) assert_raises(errors.UnboundLocalError, lambda: context.lookup_name("message"))
def can_use_aliases_with_plain_import_syntax(): node = nodes.Import([nodes.import_alias("message", "m")]) context = _update_blank_context(node, { ("message", ): module_type("message", [types.attr("value", types.str_type)]) }) assert_equal(types.str_type, context.lookup_name("m").attrs.type_of("value")) assert_raises(errors.UnboundLocalError, lambda: context.lookup_name("message"))
def can_import_module_using_import_from_syntax(): node = nodes.import_from(["."], [nodes.import_alias("message", None)]) message_module = module_type("message", [types.attr("value", types.str_type)]) context = _update_blank_context(node, { (".", "message"): message_module, }) assert_equal(types.str_type, context.lookup_name("message").attrs.type_of("value"))
def type_of_structural_type_is_as_definition(): type_bindings = { "str": types.meta_type(types.str_type), "int": types.meta_type(types.int_type), } node = nodes.structural_type("Song", [ ("description", nodes.ref("str")), ("length", nodes.ref("int")), ]) context = update_context(node, type_bindings=type_bindings) declared_type = context.lookup_name("Song") assert types.is_meta_type(declared_type) expected_type = types.structural_type("Song", [ types.attr("description", types.str_type), types.attr("length", types.int_type), ]) assert types.is_equivalent_type(expected_type, declared_type.type)
def instantiating_type_replaces_type_in_attributes_of_instantiated_attributes(self): one = types.generic_class("one", ["A"]) two = types.generic_class("two", ["B"]) three = types.generic_class("three", ["C"], lambda C: [ types.attr("value", one(two(C))), ]) assert_equal( one(two(types.int_type)), three(types.int_type).attrs.type_of("value"), )
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 invariant_type_parameter_can_be_unified_when_part_of_recursive_structural_type(self): invariant_type_param = types.invariant("T") recursive = types.generic_structural_type("recursive", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], recursive(T))), ]) assert types.is_sub_type( recursive(invariant_type_param), recursive(types.int_type), unify=[invariant_type_param] )
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 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 importing_module_in_package_mutates_that_package_in_importing_module_only(): node = nodes.Import([nodes.import_alias("messages.hello", None)]) messages_module = module_type("messages") hello_module = module_type("messages.hello", [types.attr("value", types.str_type)]) context = _update_blank_context(node, { ("messages", ): messages_module, ("messages", "hello"): hello_module, }) assert_equal(types.str_type, context.lookup_name("messages").attrs.type_of("hello").attrs.type_of("value")) assert "hello" not in messages_module.attrs
def builtin_modules_are_typed(): cgi_module = BuiltinModule("cgi", types.module("cgi", [ types.attr("escape", types.none_type), ])) node = nodes.Import([nodes.import_alias("cgi", None)]) context = update_blank_context( node, module_resolver=FakeModuleResolver({("cgi",): cgi_module}), module_types=FakeModuleTypes({}), ) assert_equal(types.none_type, context.lookup_name("cgi").attrs.type_of("escape"))
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 can_import_module_after_importing_parent_package(): messages_module = module_type("messages") hello_module = module_type("messages.hello", [types.attr("value", types.str_type)]) modules = { ("messages", ): messages_module, ("messages", "hello"): hello_module, } context = _update_blank_context([ nodes.Import([nodes.import_alias("messages", None)]), nodes.Import([nodes.import_alias("messages.hello", None)]) ], modules) assert_equal(types.str_type, context.lookup_name("messages").attrs.type_of("hello").attrs.type_of("value")) assert "hello" not in messages_module.attrs
def test_module_exports_are_set_directly_on_module(self): module_node = nodes.module( [nodes.assign([nodes.ref("value")], nodes.none())], is_executable=False ) module_type = types.module("blah", [types.attr("value", types.none_type)]) _assert_transform( module_node, cc.module( [ cc.declare("value"), cc.assign(cc.ref("value"), cc.none) ], is_executable=False, exported_names=["value"], ), type_lookup=[(module_node, module_type)] )
def value_in_builtin_module_can_be_resolved(self): # import cgi cgi_module = BuiltinModule( "cgi", types.module("cgi", [types.attr("escape", types.none_type)]) ) module_resolver = _module_resolver( _create_module("root/main.py", is_executable=True), builtin_modules={"cgi": cgi_module} ) resolved_import = module_resolver.resolve_import_value( ["cgi"], "escape", ) assert_is(cgi_module, resolved_import.module) assert_equal("escape", resolved_import.attr_name) assert_equal(["cgi"], resolved_import.module_name)
def instantiating_type_replaces_type_in_attributes(self): generic_type = types.generic_structural_type("box", ["T"], lambda T: [ types.attr("value", T) ]) assert_equal(int_type, generic_type(int_type).attrs.type_of("value"))
def assignment_to_attribute_allows_subtype(): cls = types.class_type("X", [types.attr("y", types.object_type, read_only=False)]) node = nodes.assign([nodes.attr(nodes.ref("x"), "y")], nodes.str_literal("Hello")) type_bindings = {"x": cls} update_context(node, type_bindings=type_bindings)
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 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 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))