Ejemplo n.º 1
0
    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})
Ejemplo n.º 2
0
    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})
Ejemplo n.º 3
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})
Ejemplo n.º 4
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)
Ejemplo n.º 5
0
    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})
Ejemplo n.º 6
0
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)


        """)
Ejemplo n.º 7
0
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)


        """
    )
Ejemplo n.º 8
0
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)


        """)
Ejemplo n.º 9
0
 def check(self, graph: ImportGraph) -> ContractCheck:
     return ContractCheck(kept=True)