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. |