Beispiel #1
0
    def _build_layer_chain_data(self, higher_layer_package: Module,
                                lower_layer_package: Module,
                                graph: ImportGraph) -> Dict[str, Any]:
        layer_chain_data = {
            "higher_layer": higher_layer_package.name,
            "lower_layer": lower_layer_package.name,
            "chains": [],
        }
        assert isinstance(layer_chain_data["chains"],
                          list)  # For type checker.

        chains = graph.find_shortest_chains(importer=lower_layer_package.name,
                                            imported=higher_layer_package.name)
        if chains:
            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
                    })

                layer_chain_data["chains"].append(chain_data)
        return layer_chain_data
    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})
Beispiel #3
0
    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})
Beispiel #5
0
    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})
Beispiel #6
0
 def __init__(self, graph: ImportGraph) -> None:
     self.graph = graph
     self.could_not_run = False
     self.invalid_contract_options: Dict[str, InvalidContractOptions] = {}
     self.contains_failures = False
     self.contracts: List[Contract] = []
     self._check_map: Dict[Contract, ContractCheck] = {}
     self.broken_count = 0
     self.kept_count = 0
     self.module_count = len(graph.modules)
     self.import_count = graph.count_imports()
Beispiel #7
0
def add_imports(graph: ImportGraph, import_details: List[Dict[str, Union[str, int]]]) -> None:
    """
    Adds the supplied import details to the graph.

    Intended to be the reverse of pop_imports, so the following code should leave the
    graph unchanged:

        import_details = pop_imports(graph, imports)
        add_imports(graph, import_details)
    """
    for details in import_details:
        assert isinstance(details["importer"], str)
        assert isinstance(details["imported"], str)
        assert isinstance(details["line_number"], int)
        assert isinstance(details["line_contents"], str)
        graph.add_import(
            importer=details["importer"],
            imported=details["imported"],
            line_number=details["line_number"],
            line_contents=details["line_contents"],
        )
Beispiel #8
0
def pop_imports(
    graph: ImportGraph, imports: Iterable[DirectImport]
) -> List[Dict[str, Union[str, int]]]:
    """
    Removes the supplied direct imports from the graph.

    Returns:
        The list of import details that were removed, including any additional metadata.

    Raises:
        MissingImport if the import is not present in the graph.
    """
    removed_imports: List[Dict[str, Union[str, int]]] = []
    for import_to_remove in imports:
        import_details = graph.get_import_details(
            importer=import_to_remove.importer.name, imported=import_to_remove.imported.name
        )
        if not import_details:
            raise MissingImport(f"Ignored import {import_to_remove} not present in the graph.")
        removed_imports.extend(import_details)
        graph.remove_import(
            importer=import_to_remove.importer.name, imported=import_to_remove.imported.name
        )
    return removed_imports