diff --git a/docs/usage.rst b/docs/usage.rst index 9292425e..a7d59a75 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -255,8 +255,10 @@ Higher level analysis around. Additionally, multiple layers can be grouped together at the same level; for example ``mypackage.utils`` and - ``mypackage.logging`` might sit at the bottom, so they cannot import from any other layers. Layers at the same level - must be *independent*: any dependencies in either direction are treated as illegal. + ``mypackage.logging`` might sit at the bottom, so they cannot import from any other layers. When a simple set + of sibling layers is passed then they must be independent, so any dependencies in either direction will be + treated as illegal. To allow imports between siblings in a layer then a ``Level`` dict can be passed instead, + and the ``independent`` key set to ``False``. Note: each returned :class:`.PackageDependency` does not include all possible illegal :class:`.Route` objects. Instead, once an illegal :class:`.Route` is found, the algorithm will temporarily remove it from the graph before continuing @@ -277,12 +279,24 @@ Higher level analysis ), ) - Example with sibling layers:: + Example with independent sibling layers:: dependencies = graph.find_illegal_dependencies_for_layers( layers=( "red", - {"green", "blue"}, # Green and blue should be independent. + # Imports between green and blue are forbidden. + {"green", "blue"}, + "yellow", + ), + ) + + Example with sibling layers that allow imports between the siblings:: + + dependencies = graph.find_illegal_dependencies_for_layers( + layers=( + "red", + # Imports between green and blue are allowed. + dict(independent=False, layers={"green", "blue"}), "yellow", ), ) @@ -301,8 +315,8 @@ Higher level analysis }, ) - :param Sequence[str | set[str]] layers: A sequence, each element of which consists either of the name of a layer - module, or a set of sibling layers that at the same level. If ``containers`` are also specified, + :param Sequence[str | set[str] | Level] layers: A sequence, each element of which consists either of the name of a layer + module, a set of sibling layers, or a ``Level`` dict. If ``containers`` are also specified, then these names must be relative to the container. The order is from higher to lower level layers. *Any layers that don't exist in the graph will be ignored.* :param set[str] containers: The parent modules of the layers, as absolute names that you could @@ -314,6 +328,18 @@ Higher level analysis :rtype: ``set[PackageDependency]``. :raises grimp.exceptions.NoSuchContainer: if a container is not a module in the graph. + .. class:: Level (TypedDict) + + A level within a layered architecture. + + .. attribute:: independent + + ``bool``: Whether the sibling layers within this level are required to be independent. + + .. attribute:: layers + + ``set[str]``: The sibling layers within this level. + .. class:: PackageDependency A collection of import dependencies from one Python package to another. diff --git a/src/grimp/application/ports/graph.py b/src/grimp/application/ports/graph.py index 272d77cd..cca74997 100644 --- a/src/grimp/application/ports/graph.py +++ b/src/grimp/application/ports/graph.py @@ -288,13 +288,15 @@ def find_illegal_dependencies_for_layers( Additionally, multiple layers can be grouped together at the same level; for example `mypackage.utils` and `mypackage.logging` might sit at the bottom, so they cannot - import from any other layers. Layers at the same level must be independent, so any - dependencies in either direction will be treated as illegal. + import from any other layers. When a simple set of sibling layers is passed then they + must be independent, so any dependencies in either direction will be treated as illegal. + To allow imports between siblings in a layer then a `Level` dict can be passed instead, + and the `independent` key set to `False`. Arguments: - layers: A sequence, each element of which consists either of the name of a layer - module, or a set of sibling layers that at the same level. If containers + module, a set of sibling layers, or a ``Level`` dict. If containers are also specified, then these names must be relative to the container. The order is from higher to lower level layers. Any layers that don't exist in the graph will be ignored.