def check(self, graph: ImportGraph) -> ContractCheck: is_kept = True invalid_chains = [] direct_imports_to_ignore = self.ignore_imports if self.ignore_imports else [] helpers.pop_imports(graph, direct_imports_to_ignore) # type: ignore if self.containers: self._validate_containers(graph) else: self._check_all_containerless_layers_exist(graph) for ( higher_layer_package, lower_layer_package, container, ) in self._generate_module_permutations(graph): layer_chain_data = self._build_layer_chain_data( higher_layer_package=higher_layer_package, lower_layer_package=lower_layer_package, container=container, graph=graph, ) if layer_chain_data["chains"]: is_kept = False invalid_chains.append(layer_chain_data) return ContractCheck(kept=is_kept, metadata={"invalid_chains": invalid_chains})
def check(self, graph: ImportGraph) -> ContractCheck: is_kept = True invalid_chains = [] helpers.pop_imports( graph, self.ignore_imports if self.ignore_imports else [] # type: ignore ) self._check_all_modules_exist_in_graph(graph) self._check_external_forbidden_modules(graph) # We only need to check for illegal imports for forbidden modules that are in the graph. forbidden_modules_in_graph = [ m for m in self.forbidden_modules if m.name in graph.modules # type: ignore ] for source_module in self.source_modules: # type: ignore for forbidden_module in forbidden_modules_in_graph: subpackage_chain_data = { "upstream_module": forbidden_module.name, "downstream_module": source_module.name, "chains": [], } chains = graph.find_shortest_chains( importer=source_module.name, imported=forbidden_module.name) if chains: if self._allow_indirect_imports(): chains = [chain for chain in chains if len(chain) <= 2] if len(chains) == 0: continue is_kept = False for chain in chains: chain_data = [] for importer, imported in [ (chain[i], chain[i + 1]) for i in range(len(chain) - 1) ]: import_details = graph.get_import_details( importer=importer, imported=imported) line_numbers = tuple(j["line_number"] for j in import_details) chain_data.append({ "importer": importer, "imported": imported, "line_numbers": line_numbers, }) subpackage_chain_data["chains"].append(chain_data) if subpackage_chain_data["chains"]: invalid_chains.append(subpackage_chain_data) return ContractCheck(kept=is_kept, metadata={"invalid_chains": invalid_chains})
def check(self, graph: ImportGraph) -> ContractCheck: forbidden_import_details = graph.get_import_details( importer=self.importer.name, imported=self.imported.name # type: ignore ) import_exists = bool(forbidden_import_details) return ContractCheck( kept=not import_exists, metadata={"forbidden_import_details": forbidden_import_details})
def check(self, graph: ImportGraph) -> ContractCheck: number_of_modules: int = int(self.number_of_modules) # type: ignore number_of_imports: int = int(self.number_of_imports) # type: ignore if not all([ number_of_modules == len(graph.modules), number_of_imports == graph.count_imports() ]): raise RuntimeError("Contract was mutated.") # Mutate graph. graph.add_import(importer="added-by-contract-1", imported="added-by-contract-2") return ContractCheck(kept=True)
def check(self, graph: ImportGraph) -> ContractCheck: is_kept = True invalid_chains = [] removed_imports = helpers.pop_imports( graph, self.ignore_imports if self.ignore_imports else [] # type: ignore ) self._check_all_modules_exist_in_graph(graph) for subpackage_1, subpackage_2 in permutations(self.modules, r=2): # type: ignore subpackage_chain_data = { "upstream_module": subpackage_2.name, "downstream_module": subpackage_1.name, "chains": [], } assert isinstance(subpackage_chain_data["chains"], list) # For type checker. chains = graph.find_shortest_chains(importer=subpackage_1.name, imported=subpackage_2.name) if chains: is_kept = False for chain in chains: chain_data = [] for importer, imported in [(chain[i], chain[i + 1]) for i in range(len(chain) - 1)]: import_details = graph.get_import_details( importer=importer, imported=imported) line_numbers = tuple(j["line_number"] for j in import_details) chain_data.append({ "importer": importer, "imported": imported, "line_numbers": line_numbers, }) subpackage_chain_data["chains"].append(chain_data) if subpackage_chain_data["chains"]: invalid_chains.append(subpackage_chain_data) helpers.add_imports(graph, removed_imports) return ContractCheck(kept=is_kept, metadata={"invalid_chains": invalid_chains})
def test_render_broken_contract(): settings.configure(PRINTER=FakePrinter()) contract = ForbiddenContract( name="Forbid contract", session_options={"root_packages": ["mypackage"]}, contract_options={ "source_modules": ("mypackage.one", "mypackage.two", "mypackage.three"), "forbidden_modules": ( "mypackage.blue", "mypackage.green", "mypackage.yellow", "mypackage.purple", ), }, ) check = ContractCheck( kept=False, metadata={ "invalid_chains": [ { "upstream_module": "mypackage.purple", "downstream_module": "mypackage.two", "chains": [[ { "importer": "mypackage.two", "imported": "mypackage.utils", "line_numbers": (9, ), }, { "importer": "mypackage.utils", "imported": "mypackage.purple", "line_numbers": (1, ), }, ]], }, { "upstream_module": "mypackage.green", "downstream_module": "mypackage.three", "chains": [[{ "importer": "mypackage.three", "imported": "mypackage.green", "line_numbers": (4, ), }]], }, ] }, ) contract.render_broken_contract(check) settings.PRINTER.pop_and_assert(""" mypackage.two is not allowed to import mypackage.purple: - mypackage.two -> mypackage.utils (l.9) mypackage.utils -> mypackage.purple (l.1) mypackage.three is not allowed to import mypackage.green: - mypackage.three -> mypackage.green (l.4) """)
def test_render_broken_contract(): settings.configure(PRINTER=FakePrinter()) contract = LayersContract( name="Layers contract", session_options={"root_packages": ["mypackage"]}, contract_options={"containers": ["mypackage"], "layers": ["high", "medium", "low"]}, ) check = ContractCheck( kept=False, metadata={ "invalid_chains": [ { "higher_layer": "mypackage.high", "lower_layer": "mypackage.low", "chains": [ [ { "importer": "mypackage.low.blue", "imported": "mypackage.utils.red", "line_numbers": (8, 16), }, { "importer": "mypackage.utils.red", "imported": "mypackage.utils.yellow", "line_numbers": (1,), }, { "importer": "mypackage.utils.yellow", "imported": "mypackage.high.green", "line_numbers": (3,), }, ], [ { "importer": "mypackage.low.purple", "imported": "mypackage.high.brown", "line_numbers": (9,), } ], ], }, { "higher_layer": "mypackage.medium", "lower_layer": "mypackage.low", "chains": [ [ { "importer": "mypackage.low.blue", "imported": "mypackage.medium.yellow", "line_numbers": (6,), } ] ], }, { "higher_layer": "mypackage.high", "lower_layer": "mypackage.medium", "chains": [ [ { "importer": "mypackage.medium", "imported": "mypackage.high.cyan.alpha", "line_numbers": (2,), } ] ], }, ] }, ) contract.render_broken_contract(check) settings.PRINTER.pop_and_assert( """ mypackage.low is not allowed to import mypackage.high: - mypackage.low.blue -> mypackage.utils.red (l.8, l.16) mypackage.utils.red -> mypackage.utils.yellow (l.1) mypackage.utils.yellow -> mypackage.high.green (l.3) - mypackage.low.purple -> mypackage.high.brown (l.9) mypackage.low is not allowed to import mypackage.medium: - mypackage.low.blue -> mypackage.medium.yellow (l.6) mypackage.medium is not allowed to import mypackage.high: - mypackage.medium -> mypackage.high.cyan.alpha (l.2) """ )
def test_render_broken_contract(): settings.configure(PRINTER=FakePrinter()) contract = IndependenceContract( name="Independence contract", session_options={"root_packages": ["mypackage"]}, contract_options={ "modules": ["mypackage.blue", "mypackage.green", "mypackage.yellow"] }, ) check = ContractCheck( kept=False, metadata={ "invalid_chains": [ { "upstream_module": "mypackage.yellow", "downstream_module": "mypackage.blue", "chains": [ [ { "importer": "mypackage.blue.foo", "imported": "mypackage.utils.red", "line_numbers": (16, 102), }, { "importer": "mypackage.utils.red", "imported": "mypackage.utils.brown", "line_numbers": (1, ), }, { "importer": "mypackage.utils.brown", "imported": "mypackage.yellow.bar", "line_numbers": (3, ), }, ], [{ "importer": "mypackage.blue.bar", "imported": "mypackage.yellow.baz", "line_numbers": (5, ), }], ], }, { "upstream_module": "mypackage.green", "downstream_module": "mypackage.yellow", "chains": [[{ "importer": "mypackage.yellow.foo", "imported": "mypackage.green.bar", "line_numbers": (15, ), }]], }, ] }, ) contract.render_broken_contract(check) settings.PRINTER.pop_and_assert(""" mypackage.blue is not allowed to import mypackage.yellow: - mypackage.blue.foo -> mypackage.utils.red (l.16, l.102) mypackage.utils.red -> mypackage.utils.brown (l.1) mypackage.utils.brown -> mypackage.yellow.bar (l.3) - mypackage.blue.bar -> mypackage.yellow.baz (l.5) mypackage.yellow is not allowed to import mypackage.green: - mypackage.yellow.foo -> mypackage.green.bar (l.15) """)
def check(self, graph: ImportGraph) -> ContractCheck: return ContractCheck(kept=True)