def testInvalidMapEntry(self): with file_utils.Tempdir() as d: imports_info = "%s %s\n" % ("a/b/c.pyi", d["a/b/c.pyi"]) d.create_file("imports_info", imports_info) output = os.path.join(d.path, "a/b.pyi") with self.assertRaises(ValueError): imports_map_loader.build_imports_map(d["imports_info"], output)
def test_import_map_filter(self): with file_utils.Tempdir() as d: imp_path = ".".join(d.path[1:].split("/")) init_body = """ from {0}.foo import bar from {0}.foo import baz Qux = bar.Quack """.format(imp_path) init_fn = d.create_file("foo/__init__.py", init_body) initpyi_fn = d.create_file("foo/__init__.pyi~", """ from typing import Any bar = ... # type: Any baz = ... # type: Any Qux = ... # type: Any """) bar_fn = d.create_file("foo/bar.py", "class Quack(object): pass") barpyi_fn = d.create_file("foo/bar.pyi", "class Quack(object): pass") imports_fn = d.create_file("imports_info", """ {0} {1} {2} {3} """.format(init_fn[1:-3], initpyi_fn, bar_fn[1:-3], barpyi_fn)) imports_map = imports_map_loader.build_imports_map(imports_fn, init_fn) ty = self.Infer(""" from {0}.foo import bar Adz = bar.Quack """.format(imp_path), deep=False, imports_map=imports_map, pythonpath=[""]) self.assertTypesMatchPytd(ty, """ from typing import Any, Type bar = ... # type: module Adz = ... # type: Type[{0}.foo.bar.Quack] """.format(imp_path))
def testImportsInfoFilter(self): """Test filtering out the current target's entry from the imports info.""" with utils.Tempdir() as d: # The files in our "program" that we're building an imports_map for. files = [ "a/__init__.py", "a/b.py", ] # The files the previous files are mapped to: imports = ["prefix{0}/{1}~suffix".format(d.path, f) for f in files] # Since we're calling _validate_map (via build_imports_map), the files # have to actually exist. for f in files + imports: d.create_file(f, "") # We have to add the path so the import map contains the actual files as # they exist in the tempdir. imports_map = [ "%s %s" % (d[f], d[t]) for f, t in zip(files, imports) ] d.create_file("imports_info", "\n".join(imports_map)) # build_imports_map should strip out the entry for a/__init__.py, leaving # the entry for a/b.py intact. self.assertSameElements( imports_map_loader.build_imports_map( d["imports_info"], d["a/__init__.py"]).items(), [ ("%s/a/b" % d.path, "{0}/prefix{0}/a/b.py~suffix".format( d.path)), # These are all added by the last bit of build_imports_map ("__init__", os.devnull), ("tmp/__init__", os.devnull), ("%s/__init__" % d.path[1:], os.devnull), ("%s/a/__init__" % d.path[1:], os.devnull), ])
def test_submodule_lookup(self): # Tests a common Blaze pattern: when mod/__init__.py and mod/submod.py are # in the same target, they are analyzed twice, and we should not use the # first-pass __init__.pyi to look up types for the second pass, as the # former contains a 'submod: Any' entry that masks the actual submodule. # The "%s" is used to silence the import error from the first pass. init_py = """ from mod import submod%s X = submod.X """ submod_py = """ class X: pass """ init_pyi_1, _ = self.InferWithErrors( init_py % " # import-error", module_name="mod.__init__") submod_pyi_1, _ = self.InferWithErrors(submod_py, module_name="mod.submod") with file_utils.Tempdir() as d: init_path = d.create_file( "mod/__init__.pyi", pytd_utils.Print(init_pyi_1)) submod_path = d.create_file( "mod/submod.pyi", pytd_utils.Print(submod_pyi_1)) imports_info = d.create_file("imports_info", f""" mod/__init__ {init_path} mod/submod {submod_path} """) imports_map = imports_map_loader.build_imports_map(imports_info) init_pyi = self.Infer( init_py % "", imports_map=imports_map, module_name="mod.__init__") self.assertTypesMatchPytd(init_pyi, """ from mod import submod from typing import Type X: Type[mod.submod.X] """)
def test_do_not_filter(self): with file_utils.Tempdir() as d: d.create_file("a/b/c.pyi") imports_info = "%s %s\n" % ("a/b/c.pyi", d["a/b/c.pyi"]) d.create_file("imports_info", imports_info) imports_map = imports_map_loader.build_imports_map(d["imports_info"]) self.assertEqual(imports_map["a/b/c"], d["a/b/c.pyi"])
def testDoNotFilter(self): with file_utils.Tempdir() as d: d.create_file("a/b/c.pyi") imports_info = "%s %s\n" % ("a/b/c.pyi", d["a/b/c.pyi"]) d.create_file("imports_info", imports_info) output = os.path.join(d.path, "a/b.pyi") imports_map = imports_map_loader.build_imports_map( d["imports_info"], output) self.assertEqual(imports_map["a/b/c"], d["a/b/c.pyi"])
def _store_imports_map(self, imports_map): """Postprocess --imports_info.""" if imports_map: if self.output_options.pythonpath not in ([], [""]): self.error("Not allowed with --pythonpath", "imports_info") self.output_options.imports_map = imports_map_loader.build_imports_map( imports_map, self.output_options.output) else: self.output_options.imports_map = None
def test_submodule_imports_info(self): # Tests that the presence of a submodule in imports_info doesn't prevent # pytype from finding attributes in a module's __init__ file. with file_utils.Tempdir() as d: empty = d.create_file("empty.pyi") imports_info = d.create_file( "imports_info", "email/_header_value_parser {}".format(empty)) imports_map = imports_map_loader.build_imports_map(imports_info) self.Check(""" from email import message_from_bytes """, imports_map=imports_map)
def test_missing_submodule(self): with file_utils.Tempdir() as d: foo = d.create_file("foo/__init__.pyi", "import bar.baz as baz") foo_bar = d.create_file("foo/bar.pyi", "y: str") imports_info = d.create_file("imports_info", f""" foo {foo} foo/bar {foo_bar} """) imports_map = imports_map_loader.build_imports_map(imports_info) self.CheckWithErrors(""" from foo import baz # import-error """, imports_map=imports_map)
def test_circular_dep(self): # This test imitates how analyze_project handles circular dependencies. # See https://github.com/google/pytype/issues/760. In the test, the circular # dep is between a module's __init__.py and a submodule to make it harder # for pytype to distinguish this case from test_submodule_lookup. # "%s" is used to silence import errors from the first-pass analysis. submod_py = """ from mod import Y%s class X: pass """ init_py = """ import typing if typing.TYPE_CHECKING: from mod.submod import X%s class Y: def __init__(self, x): # type: ('X') -> None pass """ submod_pyi_1, _ = self.InferWithErrors(submod_py % " # import-error", module_name="mod.submod") init_pyi_1, _ = self.InferWithErrors(init_py % " # import-error", module_name="mod.__init__") with file_utils.Tempdir() as d: submod_path = d.create_file("mod/submod.pyi", pytd_utils.Print(submod_pyi_1)) init_path = d.create_file("mod/__init__.pyi", pytd_utils.Print(init_pyi_1)) imports_info = d.create_file( "imports_info", f""" mod/submod {submod_path} mod/__init__ {init_path} """) imports_map = imports_map_loader.build_imports_map(imports_info) submod_pyi = self.Infer(submod_py % "", imports_map=imports_map, module_name="mod.submod") with open(submod_path, "w") as f: f.write(pytd_utils.Print(submod_pyi)) init_pyi = self.Infer(init_py % "", imports_map=imports_map, module_name="mod.__init__") self.assertTypesMatchPytd( init_pyi, """ from typing import Type typing: module X: Type[mod.submod.X] class Y: def __init__(self, x: X) -> None: ... """)
def _store_imports_info(self, imports_info): """Postprocess --imports_info.""" if imports_info: if self.pythonpath not in ([], [""]): raise optparse.OptionConflictError( "Not allowed with --pythonpath", "imports_info") self.imports_map = imports_map_loader.build_imports_map( imports_info, self.src_out) else: self.imports_map = None self.imports_info = imports_info
def test_directory_module_clash(self): with file_utils.Tempdir() as d: foo = d.create_file("foo.pyi", "x: int") foo_bar = d.create_file("foo/bar.pyi", "y: str") imports_info = d.create_file("imports_info", f""" foo {foo} foo/bar {foo_bar} """) imports_map = imports_map_loader.build_imports_map(imports_info) # When both foo.py and a foo/ package exist, the latter shadows the # former, so `import foo` gets you the (empty) foo/__init__.py. self.CheckWithErrors(""" import foo x = foo.x # module-attr """, imports_map=imports_map)
def testImportMap(self): with utils.Tempdir() as d: foo_filename = d.create_file("foo.pyi", """ bar = ... # type: int """) imports_map_filename = d.create_file("imports_map.txt", """ foo %s """ % foo_filename) imports_map = imports_map_loader.build_imports_map( imports_map_filename) ty = self.Infer("""\ from foo import bar """, deep=False, solve_unknowns=False, imports_map=imports_map, pythonpath=[""]) self.assertTypesMatchPytd(ty, """ bar = ... # type: int """)
def testImportMap(self): with utils.Tempdir() as d: foo_filename = d.create_file("foo.pyi", """ bar = ... # type: int """) imports_map_filename = d.create_file("imports_map.txt", """ foo %s """ % foo_filename) imports_map = imports_map_loader.build_imports_map( imports_map_filename) with self.Infer("""\ from foo import bar """, deep=False, solve_unknowns=False, imports_map=imports_map, pythonpath=[""]) as ty: self.assertTypesMatchPytd(ty, """ bar = ... # type: int """)
def test_invalid_map_entry(self): with file_utils.Tempdir() as d: imports_info = "%s %s\n" % ("a/b/c.pyi", d["a/b/c.pyi"]) d.create_file("imports_info", imports_info) with self.assertRaises(ValueError): imports_map_loader.build_imports_map(d["imports_info"])