Skip to content

Query Language

base_language

AccessSpecification

Bases: RelationshipSpecification, ABC

Offers functionality to specify which kind of imports between layers in a layer architecture are expected. Access is defined as the layer equivalent of import between modules, e.g. if layer L consists of module X, and layer M of module Y, layer L accesses layer M if module X import module Y.

BaseLayeredArchitecture

Bases: ABC

with_layer() -> LayerName abstractmethod

This is simply a convenience method to achieve proper typing. It can be omitted.

BaseModuleSpecifier

Bases: ABC

base_module_included_in_module_names() -> RuleApplier abstractmethod

If the diagram contains fully qualified module names.

with_base_module(name_relative_to_root: str) -> RuleApplier abstractmethod

Sets the name of the base module that was turned into an Evaluable. This enables the user to not having to include the entire name relative to the root module in the diagram. Example: root module is named 'src' Base module: my_project diagram generated for modules in src.my_project.components: src.my_project.components.A, src.my_project.components.B -> components are named 'A', 'B' in diagram name_relative_to_root: src.my_project.components

BehaviorBaseSpecification

Bases: Generic[U], ABC

Offers functionality to specify whether dependencies are expected or not.

DependencySpecification

Bases: RelationshipSpecification, ABC

Offers functionality to specify which kind of dependencies are expected.

FileRule

Bases: ABC

from_file(file_path: Path) -> BaseModuleSpecifier abstractmethod

Set the path to the file containing the rules that should be applied to the evaluable.

LayerBase

Bases: ABC

Offers an entry point to specifying detailed information about Layer Rule Subjects.

LayerDefinition

Bases: ABC

Offers functionality to define which modules a layer contains. Can either be a single or multiple modules.

containing_modules(modules: str | list[str]) -> BaseLayeredArchitecture abstractmethod

If a module is defined as belonging to layer X, then its submodules are also assumed to be part of layer X. A module that is already assigned to another layer, or that is a submodule or parent module of a module assigned to another layer, is rejected with an ImproperlyConfigured error when the modules are specified.

have_modules_with_names_matching(regex: str) -> BaseLayeredArchitecture abstractmethod

If a module is defined as belonging to layer X, then its submodules are also assumed to be part of layer X. Note that no attempt will be made to ensure that the regex patterns for different layers are mutually exclusive. Also note that regex expressions can have unexpected results if predicates apply to modules, but not their submodules. This is often the case for regex expressions with negations. Refer to the documentation of the module import feature for more details. Note that regex-defined layers cannot be checked for modules nested within other layers' modules when the layers are defined; contradictory matches may lead to a LayerMismatch once rules are applied.

LayerName

Bases: ABC

Offers functionality to specify the name of a yet to be defined layer.

LayerRuleBase

Bases: Generic[X]

Entry point to defining a rule based on a layered architecture.

LayerSpecification

Bases: Generic[LayerSpecificationSuccessor, InputTypes], ABC

Offers functionality to specify detail information about Layer Rule Subjects or Objects.

ModuleSpecification

Bases: Generic[ModuleSpecificationSuccessor], ABC

Offers functionality to specify detail information about Rule Subjects or Objects.

are_named(names: str | list[str]) -> ModuleSpecificationSuccessor abstractmethod

If multiple rule subjects are specified, this has the same effect as defining a rule per rule subject.

are_sub_modules_of(modules: str | list[str]) -> ModuleSpecificationSuccessor abstractmethod

If multiple rule subjects are specified, this has the same effect as defining a rule per rule subject.

have_name_containing(partial_name: str | list[str]) -> ModuleSpecificationSuccessor abstractmethod

[DEPRECATED] Use have_name_matching instead; method will be removed in upcoming releases. If multiple rule subjects are specified, this has the same effect as defining a rule per rule subject.

have_name_matching(regex: str) -> ModuleSpecificationSuccessor abstractmethod

Note that regex expressions can have unexpected results if predicates apply to modules, but not their submodules. This is often the case for regex expressions with negations. Refer to the documentation of the module import feature for more details. If multiple rule subjects are specified, this has the same effect as defining a rule per rule subject.

RelationshipSpecification

Bases: ABC

Base class for different types of relationships between modules, layers, etc.

RuleApplier

Bases: ABC

assert_applies(evaluable: EvaluableArchitecture) -> None abstractmethod

Calculates whether it (the rule) applies to a given EvaluableArchitecture. This means calculating which behavior is wanted and then checking this to the state the Evaluable represents.

Parameters:

Name Type Description Default
evaluable EvaluableArchitecture

module dependency structure to compare the rule against

required

Raises:

Type Description
AssertionError

if the rule does not apply to the evaluable object

RuleBase

Bases: ABC

Entry point to each architectural rule.

rule

Rule

Bases: DependencySpecification, RuleBase, BehaviorSpecification, RuleObject, RuleSubject, RuleApplier

Represents an architectural rule of the form Module1 [verb, such as 'should'] [import type, such as 'import'] Module2

layered_architecture_rule

LayerRule

Bases: RuleApplier, LayerRuleBase[LayeredArchitecture], LayerRuleSubject, LayerRuleObject, AccessSpecification, LayerBehaviorSpecification, LayerSpecification, LayerBase

Represents an architectural rule of the form Layer X [verb, such as 'should'] [access type, such as 'be accessed by'] Layer Y The modules that each layer contains are to be specified via a LayeredArchitecture object.

LayeredArchitecture

Bases: BaseLayeredArchitecture, LayerName, LayerDefinition

Can be used to define layers within an architecture that are each comprised of modules. Note that if a module X is specified as belonging to layer L, all of its submodules are assumed to be part of layer L as well.

with_layer() -> LayeredArchitecture

This is simply a convenience method to achieve proper typing. It can be omitted.

multiple_rule_applier

MultipleRuleApplier

Bases: RuleApplier

assert_applies(evaluable: EvaluableArchitecture) -> None

Checks a number of rules against the given evaluable and returns an aggregated error message if at least one tests fails.

Parameters:

Name Type Description Default
evaluable EvaluableArchitecture
required

onion_architecture

Draft of a predefined 'Onion Architecture' rule, inspired by ArchUnit's Architectures.onionArchitecture() <https://www.archunit.org/userguide/html/000_Index.html#_onion_architecture>_. It is implemented as a convenience wrapper around the layer rule syntax defined by :class:pytestarch.LayeredArchitecture and :class:pytestarch.LayerRule.

OnionArchitecture

Predefined rule assessing whether an evaluable architecture complies with the Onion Architecture style: adapters access application services, which access domain services, which access domain models. Only these components may access the domain models, only adapters and application services may access the domain services, and only adapters may access the application services. Conversely, adapters must not be accessed by anyone.

Each component is defined via the methods of this class; multiple modules belonging to the same component can be specified at once. The resulting rules are then applied to an evaluable architecture via 'assert_applies'.

Note that this does not restrict which modules a component itself imports - only who is allowed to access a given component. Also note that dependencies between modules of the same component are always allowed.

Misconfigurations - a component specified more than once, a component without modules, or a module assigned to more than one component - are rejected with an ImproperlyConfigured error as soon as they are specified. Modules nested under or above a module of another component are rejected with the same error when the equivalent layered architecture is built.

Example

onion_architecture() .domain_models("my_project.domain.model") .domain_services("my_project.domain.service") .application_services("my_project.application") .adapter("cli", "my_project.adapter.cli") .assert_applies(evaluable)

adapter(name: str, modules: str | Sequence[str]) -> OnionArchitecture

Defines which modules make up an adapter of the given name, which may access all other components but must not be accessed itself.

Parameters:

Name Type Description Default
name str

name of the adapter, e.g. 'cli'. Must be unique.

required
modules str | Sequence[str]

module or list of modules belonging to the adapter.

required

Raises:

Type Description
ImproperlyConfigured

if an adapter of the given name already exists, no modules are given, or a module already belongs to another component.

application_services(modules: str | Sequence[str]) -> OnionArchitecture

Defines which modules make up the application services, which may access the domain services and the domain models.

Parameters:

Name Type Description Default
modules str | Sequence[str]

module or list of modules belonging to the application services.

required

Raises:

Type Description
ImproperlyConfigured

if the application services are specified more than once, no modules are given, or a module already belongs to another component.

assert_applies(evaluable: EvaluableArchitecture) -> None

Applies this architecture's rules to the given evaluable. One layer rule per component is derived from the component structure according to the Onion Architecture style, e.g. 'domain model may only be accessed by the domain services, application services, and adapters'.

Parameters:

Name Type Description Default
evaluable EvaluableArchitecture

module dependency structure to compare the rules against.

required

Raises:

Type Description
ImproperlyConfigured

if no component has been specified.

AssertionError

with an aggregated error message if this architecture is violated by the evaluable. The message is prefixed with a description of the violated architecture.

domain_models(modules: str | Sequence[str]) -> OnionArchitecture

Defines which modules make up the domain models, the innermost layer of the architecture.

Parameters:

Name Type Description Default
modules str | Sequence[str]

module or list of modules belonging to the domain models.

required

Raises:

Type Description
ImproperlyConfigured

if the domain models are specified more than once, no modules are given, or a module already belongs to another component.

domain_services(modules: str | Sequence[str]) -> OnionArchitecture

Defines which modules make up the domain services, which may access the domain models.

Parameters:

Name Type Description Default
modules str | Sequence[str]

module or list of modules belonging to the domain services.

required

Raises:

Type Description
ImproperlyConfigured

if the domain services are specified more than once, no modules are given, or a module already belongs to another component.

layered_architecture() -> LayeredArchitecture

Converts this rule into the equivalent layered architecture. Each component maps to one layer. This is the basis of all rules applied by 'assert_applies' and can also be used to define additional layer rules on top of the onion architecture.

Returns:

Type Description
LayeredArchitecture

LayeredArchitecture with one layer each for the domain models, the

LayeredArchitecture

domain services, the application services, and each adapter.

Raises:

Type Description
ImproperlyConfigured

if no component has been specified.

onion_architecture() -> OnionArchitecture

Entry point to a predefined Onion Architecture rule, mirroring the structure of ArchUnit's onionArchitecture() builder.

Returns:

Type Description
OnionArchitecture

OnionArchitecture builder, to be configured via its domain_models,

OnionArchitecture

domain_services, application_services, and adapter methods.

layered_architecture

Draft of a predefined layered architecture rule, inspired by ArchUnit's Architectures.layeredArchitecture() <https://www.archunit.org/userguide/html/000_Index.html#_layered_architecture>_. It is implemented as a convenience wrapper around the layer rule syntax defined by :class:pytestarch.LayeredArchitecture and :class:pytestarch.LayerRule.

PredefinedLayeredArchitecture

Bases: MultipleRuleApplier

Predefined rule assessing whether an evaluable architecture complies with the layered architecture defined via the factory function 'layered_architecture': each layer may only access the layers listed as its allowed dependencies.

Note that this restricts outbound dependencies only - it mirrors the white list style of ArchUnit's layer rules, so a layer importing a module that belongs to no defined layer is also a violation, while dependencies within a layer are always allowed.

Misconfigurations are rejected with an ImproperlyConfigured error as soon as the architecture is constructed: unknown layer names in 'allowed_dependencies' (as subject or as allowed dependency) and layers without modules are detected here, as well as modules assigned to more than one layer and modules nested under or above a module of another layer.

Example

layered_architecture( { "controller": "my_project.controller", "service": "my_project.service", "persistence": "my_project.persistence", "model": "my_project.model", }, {"controller": ["service"], "service": ["persistence", "model"]}, ).assert_applies(evaluable)

assert_applies(evaluable: EvaluableArchitecture) -> None

Applies this architecture's rules to the given evaluable. One layer rule per entry of 'allowed_dependencies' is derived from the dependency structure, stating that the layer may only access the listed layers.

Parameters:

Name Type Description Default
evaluable EvaluableArchitecture

module dependency structure to compare the rules against.

required

Raises:

Type Description
AssertionError

with an aggregated error message if this architecture is violated by the evaluable. The message is prefixed with a description of the violated architecture.

layered_architecture() -> LayeredArchitecture

Returns the layered architecture of this rule. This is the basis of all rules applied by 'assert_applies' and can also be used to define additional layer rules on top of the layered architecture.

Returns:

Type Description
LayeredArchitecture

LayeredArchitecture with one layer per entry of 'layers'.

layered_architecture(layers: dict[str, str | Sequence[str]], allowed_dependencies: dict[str, str | Sequence[str]]) -> PredefinedLayeredArchitecture

Entry point to a predefined layered architecture rule, mirroring the structure of ArchUnit's layeredArchitecture() builder.

Each entry of 'allowed_dependencies' generates one rule enforcing that the named layer may only access the listed layers. An empty list means that the layer must not access anything, and layers omitted from 'allowed_dependencies' are defined but unrestricted.

Parameters:

Name Type Description Default
layers dict[str, str | Sequence[str]]

mapping of layer names to the module or list of modules that make up each layer.

required
allowed_dependencies dict[str, str | Sequence[str]]

mapping of layer names to the layer name or list/sequence of layer names they may access.

required

Returns:

Type Description
PredefinedLayeredArchitecture

PredefinedLayeredArchitecture rule, to be configured via the given

PredefinedLayeredArchitecture

mappings and to be applied to an evaluable architecture via

PredefinedLayeredArchitecture

'assert_applies'.

Raises:

Type Description
ImproperlyConfigured

if no layer is defined in 'layers', if 'allowed_dependencies' references a layer name that is not defined in 'layers' - as subject or as allowed dependency -, a layer's module list is empty, a module is already assigned to another layer of this architecture, or a module is nested under or is a submodule of a module assigned to another layer of this architecture. All misconfigurations are detected here, before any rule is applied.