def test_add_module(): graph = build_graph("testpackage") number_of_modules = len(graph.modules) graph.add_module("foo") assert "foo" in graph.modules assert number_of_modules + 1 == len(graph.modules)
def find_package_cycles(module_name: str, display: bool = False) -> None: sys.path.insert(0, os.getcwd()) module = grimp.Module(module_name) import_graph = grimp.build_graph(module.package_name) def package_graph(package_name: str) -> nx.DiGraph: nx_graph = nx.DiGraph() children = import_graph.find_children(package_name) for child in children: nx_graph.add_node(child) # Dependencies between children. for upstream, downstream in itertools.permutations(children, r=2): if import_graph.direct_import_exists(imported=upstream, importer=downstream, as_packages=True): nx_graph.add_edge(upstream, downstream) return nx_graph stack = [module_name] while stack: name = stack.pop() graph = package_graph(name) try: cycle = nx.find_cycle(graph, orientation="original") except nx.NetworkXNoCycle: for module_child in sorted(graph.nodes): stack.append(module_child) else: typer.secho("Package import cycle detected:", fg="red", bold=True) typer.secho(cycle_repr(cycle), fg="red") if display: draw_graph(name) sys.exit(1) typer.secho( f"Success: no package import cycles detected in {len(import_graph.modules)} modules", fg="green", bold=True )
def draw_graph(module_name: str) -> None: """ Create a file showing a graph of the supplied package. Args: module_name: the package or subpackage name of any importable Python package. """ # Add current directory to the path, as this doesn't happen automatically. sys.path.insert(0, os.getcwd()) module = grimp.Module(module_name) graph = grimp.build_graph(module.package_name) module_children = graph.find_children(module.name) dot = Digraph(format='png', node_attr={'fontname': 'helvetica'}) dot.attr( concentrate='true', # Merge lines together. ) for module_child in module_children: dot.node(module_child) # Dependencies between children. for upstream, downstream in itertools.permutations(module_children, r=2): if graph.direct_import_exists(imported=upstream, importer=downstream, as_packages=True): dot.edge(downstream, upstream) source_filename = tempfile.mkstemp()[1] dot.view(filename=source_filename, cleanup=True)
def test_remove_module(): graph = build_graph("testpackage") number_of_modules = len(graph.modules) graph.remove_module("testpackage.two.alpha") assert "testpackage.two.alpha" not in graph.modules assert number_of_modules - 1 == len(graph.modules)
def test_direct_import_exists(): graph = build_graph("testpackage") assert False is graph.direct_import_exists( importer="testpackage.one.alpha", imported="testpackage.two.alpha") assert True is graph.direct_import_exists(importer="testpackage.two.alpha", imported="testpackage.one.alpha")
def test_as_packages_false(self): graph = build_graph("testpackage") assert not graph.chain_exists(imported="testpackage.utils", importer="testpackage.one.alpha") assert graph.chain_exists(imported="testpackage.one.alpha", importer="testpackage.utils")
def test_find_children(): graph = build_graph("testpackage") assert graph.find_children("testpackage.one") == { "testpackage.one.alpha", "testpackage.one.beta", "testpackage.one.gamma", "testpackage.one.delta", }
def test_as_package_true(self): graph = build_graph("testpackage") assert graph.find_upstream_modules("testpackage.two", as_package=True) == { "testpackage.one.alpha", "testpackage.utils", "testpackage.one", }
def test_find_shortest_chain(): graph = build_graph("testpackage") assert ( "testpackage.utils", "testpackage.two.alpha", "testpackage.one.alpha", ) == graph.find_shortest_chain(importer="testpackage.utils", imported="testpackage.one.alpha")
def test_find_shortest_chains(): graph = build_graph("testpackage") assert { ("testpackage.two.alpha", "testpackage.one.alpha"), ("testpackage.two.beta", "testpackage.one.alpha"), ("testpackage.two.gamma", "testpackage.utils", "testpackage.one"), } == graph.find_shortest_chains(importer="testpackage.two", imported="testpackage.one")
def test_get_import_details(): graph = build_graph("testpackage") assert [{ "importer": "testpackage.utils", "imported": "testpackage.two.alpha", "line_number": 5, "line_contents": "from .two import alpha", }] == graph.get_import_details(importer="testpackage.utils", imported="testpackage.two.alpha")
def test_stores_import_between_root_packages(self, root_packages): graph = build_graph(*root_packages) assert [{ "importer": "rootpackagegreen.two", "imported": "rootpackageblue.one.alpha", "line_number": 1, "line_contents": "from rootpackageblue.one import alpha", }] == graph.get_import_details(importer="rootpackagegreen.two", imported="rootpackageblue.one.alpha")
def test_as_package_false(self): graph = build_graph("testpackage") assert graph.find_upstream_modules("testpackage.one.alpha") == set() assert graph.find_upstream_modules("testpackage.utils") == { "testpackage.one", "testpackage.two.alpha", "testpackage.one.alpha", }
def test_stores_import_within_package(self, root_packages): graph = build_graph(*root_packages) assert [{ "importer": "rootpackageblue.two", "imported": "rootpackageblue.one.alpha", "line_number": 1, "line_contents": "from .one.alpha import BAR", }] == graph.get_import_details(importer="rootpackageblue.two", imported="rootpackageblue.one.alpha")
def test_as_packages_true(self): graph = build_graph("testpackage") assert graph.chain_exists(imported="testpackage.one", importer="testpackage.utils", as_packages=True) assert not graph.chain_exists(imported="testpackage.utils", importer="testpackage.one", as_packages=True)
def test_find_descendants(): graph = build_graph("testpackage") assert graph.find_descendants("testpackage.one") == { "testpackage.one.alpha", "testpackage.one.beta", "testpackage.one.gamma", "testpackage.one.delta", "testpackage.one.delta.blue", }
def test_find_modules_that_directly_import(): graph = build_graph("testpackage") result = graph.find_modules_that_directly_import("testpackage.one.alpha") assert { "testpackage.one.beta", "testpackage.two.alpha", "testpackage.two.beta", } == result
def test_as_package_true(self): graph = build_graph("testpackage") result = graph.find_downstream_modules("testpackage.one", as_package=True) assert { "testpackage.two.alpha", "testpackage.two.beta", "testpackage.two.gamma", "testpackage.utils", } == result
def test_graph_has_correct_modules_regardless_of_package_order( self, root_packages): graph = build_graph(*root_packages) assert graph.modules == { "rootpackageblue", "rootpackageblue.one", "rootpackageblue.one.alpha", "rootpackageblue.two", "rootpackageblue.three", "rootpackagegreen", "rootpackagegreen.one", "rootpackagegreen.two", }
def test_add_and_remove_import(): graph = build_graph("testpackage") a = "testpackage.one.delta.blue" b = "testpackage.two.alpha" assert not graph.direct_import_exists(importer=b, imported=a) graph.add_import(importer=b, imported=a) assert graph.direct_import_exists(importer=b, imported=a) graph.remove_import(importer=b, imported=a) assert not graph.direct_import_exists(importer=b, imported=a)
def test_modules(): graph = build_graph("testpackage") assert graph.modules == { "testpackage", "testpackage.one", "testpackage.one.alpha", "testpackage.one.beta", "testpackage.one.gamma", "testpackage.one.delta", "testpackage.one.delta.blue", "testpackage.two", "testpackage.two.alpha", "testpackage.two.beta", "testpackage.two.gamma", "testpackage.utils", }
def test_build_graph_of_non_ascii_source(): """ Tests we can cope with non ascii Python source files. """ graph = grimp.build_graph("encodingpackage") result = graph.get_import_details( importer="encodingpackage.importer", imported="encodingpackage.imported" ) assert [ { "importer": "encodingpackage.importer", "imported": "encodingpackage.imported", "line_number": 1, "line_contents": "from .imported import π", }, ] == result
def build(self, root_package_names: List[str], include_external_packages: bool = False) -> ImportGraph: return grimp.build_graph( *root_package_names, include_external_packages=include_external_packages)
def test_build_graph_on_external_package(package_name): # All we care about is whether or not the graph builds without raising an exception. grimp.build_graph(package_name)
def test_find_modules_directly_imported_by(): graph = build_graph("testpackage") result = graph.find_modules_directly_imported_by("testpackage.utils") assert {"testpackage.one", "testpackage.two.alpha"} == result