def _build_graph(self): graph = ImportGraph() for module in ( "mypackage", "mypackage.high", "mypackage.high.green", "mypackage.high.blue", "mypackage.high.yellow", "mypackage.high.yellow.alpha", "mypackage.medium", "mypackage.medium.orange", "mypackage.medium.orange.beta", "mypackage.medium.red", "mypackage.low", "mypackage.low.black", "mypackage.low.white", "mypackage.low.white.gamma", ): graph.add_module(module) # Add some 'legal' imports. graph.add_import(importer="mypackage.high.green", imported="mypackage.medium.orange") graph.add_import(importer="mypackage.high.green", imported="mypackage.low.white.gamma") graph.add_import(importer="mypackage.medium.orange", imported="mypackage.low.white") graph.add_import(importer="mypackage.high.blue", imported="mypackage.utils") graph.add_import(importer="mypackage.utils", imported="mypackage.medium.red") return graph
def test_ignore_imports_tolerates_duplicates(): graph = ImportGraph() graph.add_module("mypackage") graph.add_import(importer="mypackage.a", imported="mypackage.b", line_number=1, line_contents="-") graph.add_import(importer="mypackage.a", imported="mypackage.c", line_number=2, line_contents="-") contract = IndependenceContract( name="Independence contract", session_options={"root_packages": ["mypackage"]}, contract_options={ "modules": ("mypackage.a", "mypackage.b"), "ignore_imports": [ "mypackage.a -> mypackage.b", "mypackage.a -> mypackage.c", "mypackage.a -> mypackage.b", ], }, ) contract_check = contract.check(graph=graph) assert contract_check.kept
def test_top_level_import(self): graph = ImportGraph() graph.add_import(importer="green", imported="blue") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green", "blue")}
def test_count_imports(imports, expected_count): graph = ImportGraph() for importer, imported in imports: graph.add_import(importer=importer, imported=imported) assert expected_count == graph.count_imports()
def _build_legal_graph(self, container=None): graph = ImportGraph() if container: graph.add_module(container) namespace = f"{container}." else: namespace = "" for module in ( f"{namespace}high", f"{namespace}high.green", f"{namespace}high.blue", f"{namespace}high.yellow", f"{namespace}high.yellow.alpha", f"{namespace}medium", f"{namespace}medium.orange", f"{namespace}medium.orange.beta", f"{namespace}medium.red", f"{namespace}low", f"{namespace}low.black", f"{namespace}low.white", f"{namespace}low.white.gamma", ): graph.add_module(module) # Add some 'legal' imports. graph.add_import(importer=f"{namespace}high.green", imported=f"{namespace}medium.orange") graph.add_import(importer=f"{namespace}high.green", imported=f"{namespace}low.white.gamma") graph.add_import(importer=f"{namespace}medium.orange", imported=f"{namespace}low.white") graph.add_import(importer=f"{namespace}high.blue", imported=f"{namespace}utils") graph.add_import(importer=f"{namespace}utils", imported=f"{namespace}medium.red") return graph
def test_returns_empty_list_when_import_but_no_available_details(self): graph = ImportGraph() importer, imported = "foo", "bar" graph.add_import(importer=importer, imported=imported), assert [] == graph.get_import_details(importer=importer, imported=imported)
def test_import_between_top_level_and_child(self): graph = ImportGraph() graph.add_module("blue") graph.add_import(importer="green", imported="blue.foo") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green", "blue.foo")}
def test_find_shortest_chain_returns_none_if_not_exists(): graph = ImportGraph() a, b, c = "foo", "bar", "baz" graph.add_import(importer=a, imported=b) graph.add_import(importer=b, imported=c) assert None is graph.find_shortest_chain(importer=c, imported=a)
def test_does_nothing_if_module_is_already_squashed(self): graph = ImportGraph() graph.add_module("foo", is_squashed=True) graph.add_import(importer="foo", imported="bar") graph.squash_module("foo") assert graph.direct_import_exists(importer="foo", imported="bar")
def test_grandchildren_import(self): graph = ImportGraph() graph.add_module("green") graph.add_module("blue") graph.add_import(importer="green.foo.one", imported="blue.bar.two") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green.foo.one", "blue.bar.two")}
def test_no_results_in_reverse_direction(self): graph = ImportGraph() graph.add_module("green") graph.add_module("blue") graph.add_import(importer="green.foo", imported="blue.bar") result = graph.find_shortest_chains(importer="blue", imported="green") assert result == set()
def test_first_level_child_import(self): graph = ImportGraph() graph.add_module("green") graph.add_module("blue") graph.add_import(importer="green.foo", imported="blue.bar") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green.foo", "blue.bar")}
def test_short_indirect_import(self): graph = ImportGraph() graph.add_module("green") graph.add_module("blue") graph.add_import(importer="green.indirect", imported="purple") graph.add_import(importer="purple", imported="blue.foo") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green.indirect", "purple", "blue.foo")}
def test_remove_import(): graph = ImportGraph() a, b, c = "foo", "bar", "baz" graph.add_import(importer=a, imported=b) graph.add_import(importer=a, imported=c) graph.remove_import(importer=a, imported=b) assert {a, b, c} == graph.modules assert {c} == graph.find_modules_directly_imported_by(a)
def test_doesnt_error_if_imports_within_module(self): graph = ImportGraph() for module in [ "foo", "foo.green", "foo.blue", ]: graph.add_module(module) graph.add_import(importer="foo.blue", imported="foo.green") graph.squash_module("foo")
def test_keeps_import_of_squashed_root(self): graph = ImportGraph() for module in [ "foo", "foo.green", "bar.blue", ]: graph.add_module(module) graph.add_import(importer="bar.blue", imported="foo") graph.squash_module("foo") assert graph.direct_import_exists(importer="bar.blue", imported="foo")
def test_contracts_import_to_descendant(self): graph = ImportGraph() for module in [ "foo", "foo.green", "bar.blue", ]: graph.add_module(module) graph.add_import(importer="bar.blue", imported="foo.green") graph.squash_module("foo") assert graph.direct_import_exists(importer="bar.blue", imported="foo")
def test_remove_module(): graph = ImportGraph() a, b = {"mypackage.blue", "mypackage.green"} graph.add_module(a) graph.add_module(b) graph.add_import(importer=a, imported=b) graph.remove_module(b) assert {a} == graph.modules # Removing a non-existent module doesn't cause an error. graph.remove_module("mypackage.yellow")
def test_add_import(add_module): graph = ImportGraph() a, b = "foo", "bar" # Adding the module should make no difference to the result. if add_module: graph.add_module(a) graph.add_import(importer=a, imported=b) assert {a, b} == graph.modules assert {b} == graph.find_modules_directly_imported_by(a) assert set() == graph.find_modules_directly_imported_by(b)
def _build_default_graph(self): graph = ImportGraph() # Add 26 modules. for letter in string.ascii_lowercase: graph.add_module(f"mypackage.{letter}") # Add 10 imports in total. for imported in ("d", "e", "f"): for importer in ("a", "b", "c"): graph.add_import( importer=f"mypackage.{importer}", imported=f"mypackage.{imported}" ) # 3 * 3 = 9 imports. graph.add_import(importer="mypackage.d", imported="mypackage.f") # 1 extra import. return graph
def _build_graph(self): graph = ImportGraph() for module in ( "one", "one.alpha", "two", "three", "blue", "green", "green.beta", "yellow", "purple", "utils", ): graph.add_module(f"mypackage.{module}") for external_module in ("sqlalchemy", "requests"): graph.add_module(external_module, is_squashed=True) graph.add_import( importer="mypackage.one.alpha", imported="mypackage.green.beta", line_number=3, line_contents="foo", ) graph.add_import( importer="mypackage.three", imported="mypackage.green", line_number=4, line_contents="foo", ) graph.add_import( importer="mypackage.two", imported="mypackage.utils", line_number=9, line_contents="foo", ) graph.add_import( importer="mypackage.utils", imported="mypackage.purple", line_number=1, line_contents="foo", ) graph.add_import(importer="mypackage.three", imported="sqlalchemy", line_number=1, line_contents="foo") return graph
def test_graph_can_be_mutated_without_affecting_other_contracts(self): # The MutationCheckContract checks that there are a certain number of modules and imports # in the graph, then adds one more module and one more import. We can check two such # contracts and the second one will fail, if the graph gets mutated by other contracts. session_options = { "root_package": "mypackage", "contract_types": ["mutation_check: tests.helpers.contracts.MutationCheckContract"], } reader = FakeUserOptionReader( UserOptions( session_options=session_options, contracts_options=[ { "type": "mutation_check", "name": "Contract one", "number_of_modules": "5", "number_of_imports": "2", }, { "type": "mutation_check", "name": "Contract two", "number_of_modules": "5", "number_of_imports": "2", }, ], ) ) settings.configure( USER_OPTION_READERS=[reader], GRAPH_BUILDER=FakeGraphBuilder(), PRINTER=FakePrinter() ) graph = ImportGraph() # Create a graph with five modules and two imports. for module in ("one", "two", "three", "four", "five"): graph.add_module(module) graph.add_import(importer="one", imported="two") graph.add_import(importer="one", imported="three") settings.GRAPH_BUILDER.inject_graph(graph) result = lint_imports(is_debug_mode=True) assert result == SUCCESS
def test_import_details_to_descendant_are_lost(self): graph = ImportGraph() for module in [ "foo", "foo.green", "bar.blue", ]: graph.add_module(module) graph.add_import( importer="bar.blue", imported="foo.green", line_number=1, line_contents="from foo import green", ) graph.squash_module("foo") assert [] == graph.get_import_details(importer="bar.blue", imported="foo")
def test_long_indirect_import(self): graph = ImportGraph() graph.add_module("green") graph.add_module("blue") graph.add_import(importer="green.baz", imported="yellow.three") graph.add_import(importer="yellow.three", imported="yellow.two") graph.add_import(importer="yellow.two", imported="yellow.one") graph.add_import(importer="yellow.one", imported="blue.foo") result = graph.find_shortest_chains(importer="green", imported="blue") assert result == {("green.baz", "yellow.three", "yellow.two", "yellow.one", "blue.foo")}
def test_import_details_from_squashed_root_are_preserved(self): graph = ImportGraph() for module in [ "foo", "foo.green", "bar.blue", ]: graph.add_module(module) import_details = dict( importer="foo", imported="bar.blue", line_number=1, line_contents="from . import bar", ) graph.add_import(**import_details) graph.squash_module("foo") assert [import_details ] == graph.get_import_details(importer="foo", imported="bar.blue")
def test_ignore_imports(ignore_imports, is_kept): graph = ImportGraph() graph.add_module("mypackage") graph.add_import(importer="mypackage.a", imported="mypackage.irrelevant", line_number=1, line_contents="-") graph.add_import(importer="mypackage.a", imported="mypackage.indirect", line_number=1, line_contents="-") graph.add_import(importer="mypackage.indirect", imported="mypackage.b", line_number=1, line_contents="-") contract = IndependenceContract( name="Independence contract", session_options={"root_packages": ["mypackage"]}, contract_options={ "modules": ("mypackage.a", "mypackage.b"), "ignore_imports": ignore_imports, }, ) contract_check = contract.check(graph=graph) assert is_kept == contract_check.kept
def test_happy_path(self): graph = ImportGraph() imports_info = [ dict( importer="mypackage.foo", imported="mypackage.bar", line_number=1, line_contents="from . import bar", ), dict( importer="mypackage.foo", imported="mypackage.bar", line_number=10, line_contents="from .bar import a_function", ), ] for import_info in imports_info: graph.add_import(**import_info) assert imports_info == graph.get_import_details( importer="mypackage.foo", imported="mypackage.bar")
def test_returns_only_relevant_imports(self): graph = ImportGraph() imports_info = [ dict( importer="mypackage.foo", imported="mypackage.bar", line_number=1, line_contents="from . import bar", ) ] graph.add_import(**imports_info[0]) # Also add a different import in the same module. graph.add_import( importer="mypackage.foo", imported="mypackage.baz", line_number=2, line_contents="from . import baz", ) assert imports_info == graph.get_import_details( importer="mypackage.foo", imported="mypackage.bar")
def test_find_modules_directly_imported_by(): graph = ImportGraph() a, b, c = "foo", "bar", "baz" d, e, f = "foo.one", "bar.one", "baz.one" graph.add_import(importer=a, imported=b) graph.add_import(importer=a, imported=c) graph.add_import(importer=a, imported=d) graph.add_import(importer=b, imported=e) graph.add_import(importer=f, imported=a) assert {b, c, d} == graph.find_modules_directly_imported_by("foo")
def test_find_modules_that_directly_import(): graph = ImportGraph() a, b, c = "foo", "bar", "baz" d, e, f = "foo.one", "bar.one", "baz.one" graph.add_import(importer=a, imported=b) graph.add_import(importer=a, imported=c) graph.add_import(importer=a, imported=d) graph.add_import(importer=b, imported=e) graph.add_import(importer=f, imported=b) assert {a, f} == graph.find_modules_that_directly_import("bar")