Changelog
This project uses semantic versioning and follows keep a changelog.
[5.1.1] -- 2026-11-08
Changed
- In a container root (a
root_pathdirectory without__init__.py, e.g.src), imports between top-level packages are now classified as internal. Forbidding rules over them that previously passed vacuously may now fail; rules andexternal_exclusionsmust use the prefixed names (src.package2instead ofpackage2).
Fixed
- Absolute imports written root-relative to the scan root (e.g.
from package2 import module2below asrcroot) are now resolved to their prefixed module names (src.package2.module2) instead of staying external (partially fixes #144; module naming is unchanged). Partial scans and package roots (with__init__.py) are unaffected. A top-level internal module sharing its name with an external library (src/loggingvslogging) takes precedence.
[5.1.0] -- 2026-10-24
Added
- Experimental predefined architecture rules inspired by ArchUnit (#134):
pytestarch.experimental.layered_architecture()enforces a per-layer dependency whitelist,pytestarch.experimental.onion_architecture()implements the Onion Architecture style.
Changed
- Dependency and packaging management moved from Poetry to uv:
pyproject.tomlnow uses ahatchlingbuild backend and PEP 735 dependency groups. - Replaced mypy with pyrefly for static type checking: pyrefly runs as a pre-commit hook.
- Added a root
index.htmlthat redirects to the latest docs (https://zyskarch.github.io/pytestarch/→https://zyskarch.github.io/pytestarch/latest/). from <package> import <module>is now recognized as an import of<package>.<module>, so rules usingare_namedmatch it (#145).from .<package> import <module>is now recognized as an import of<package>.<module>as well (#145).- The bare relative form
from . import <name>now also records an import of the containing package itself, matching the absolutefrom <package> import <name>form. Rules naming the package now report the package itself; previously the imported submodule was reported, and for imported attributes and*no import was recorded at all. The level-2+ forms behave the same (from .. import <name>records the grandparent package). - A bare relative import whose level exceeds the importer's module hierarchy (such as
from .. import *in a module without parent packages) now raises anImportExceptioninstead of being silently ignored. - In partial scans (a
module_pathbelowroot_path) withexclude_external_libraries=False, internal modules outside the scannedmodule_pathare no longer added as nodes. - Overlapping layer definitions, where a module of one layer is a parent or submodule of another layer's module, are now rejected with an
ImproperlyConfigurederror at definition time, instead of aLayerMismatchonly when rules are applied. - Symlink aliases are followed during the scan: a module reached through several paths is emitted under each of them, with the target's name only when the walk also reaches the target. Exclusion and
.py-suffix matching use the walk (link) path, while file content is read from the resolved target. Directory listings and parsed ASTs are cached by resolved path, so aliases are not re-read or re-parsed; symlink cycles terminate. - Less path resolution during graph construction: only the scan start and symlink entries are resolved, and directory listings and ASTs are reused across aliases.
- Less redundant graph construction work: the per-node and per-edge flatten call, a no-op without a
level_limit, is skipped, andadd_nodeis called without a preceding membership lookup.
Fixed
from . import <name>no longer turns imported attributes or*into imports of non-existent<package>.<name>modules.- Internal modules are no longer added to the graph as spurious external dependency nodes when
exclude_external_libraries=False(partially fixes #144). - Partial scans with
exclude_external_libraries=Falseno longer create duplicate bare-name nodes for imports of the scanned module itself (appnext to the realsrc.app). - Imports of the scanned module itself are no longer classified as external when
exclude_external_libraries=True; previously only submodules counted as internal. - Root and module paths keep the path form the caller passed: anchored (regex) exclusions match again — for the first time also for files — and symlinked roots and module paths keep their link names, so a symlinked
module_pathoutside the root builds the graph again instead of crashing. - Exclusion patterns are matched against forward-slash paths on all platforms, so
src/skipdir-style patterns work on Windows as well. get_evaluable_architectureraises a clear error whenroot_pathormodule_pathdoes not exist (dangling symlinks included), instead of silently returning an empty graph in which every rule passes vacuously.get_evaluable_architecture_for_module_objectsraises a clear error when the root or module object has no source file (e.g. namespace or builtin modules), instead of crashing with aTypeError.- Passing
.as the root keeps the historical quirk of an empty module name prefix (module names such as.pkg.m); pass an explicit path for clean names. - Imports inside
else,elif,except,finallyandmatch/caseblocks are now recognized. Rules that only passed because these imports were missed can now be violated. - An importee that starts with the root module name but is not a submodule of it (e.g.
appdirsfor a root moduleapp) is no longer misclassified as internal. - The layer rule now matches modules on exact dot-separated boundaries:
project.model_utilsis no longer treated as part of layerproject.model, so accesses to it are detected. - Plot label aliases now apply only to the aliased module and its dot-separated submodules; previously they also relabeled name-prefix matches such as
appdirsfor an alias onapp. regex_exclusions=None(theget_evaluable_architecturedefault) is accepted as no exclusions; previously it raised aTypeErrorunlessexclusionswas set.- Membership checks on the dependency graph raise a
TypeErrorfor malformed inputs, i.e. anything that is not a module name or a pair of two module names; previously such inputs were silently looked up, producing confusing downstream errors.
[5.0.0] -- 2026-10-11
Changed
- Drop support for python 3.9
- Drop support for python 3.10
- Support for python 3.14
- Support for python 3.15
- Increased minimum dependency versions: networkx >=3.6, matplotlib >=3.11
- Accept
pathlib.Paths inget_evaluable_architecture(..) - Updated development dependencies
- Readme examples and links to documentation
- Documentation is now built with zensical instead of mkdocs
Fixed
- Straightforward error message when using wildcards in
are_namedrules. - Typo in docstring of
get_evaluable_architecture(..). - Module filter uses regex when activated instead of only if last module.
- Descriptive error message with actionable guidance when a queried module is not present in the dependency graph.
- Parser no longer errors out on windows due to unspecified encoding.
- Docstring formatting in the API reference: dependency structure examples in the rule assessment docs and the PlantUML arrow operators are now rendered as code.
- Docstring argument mismatches: actual arguments and docstring diverged, so the generated configuration was incomplete.
site_urlis configured, so the generated sitemap lists all pages and pages define canonical URLs.
[4.0.1] -- 2025-08-08
Fixed
- KeyError when not including all layers with regexes in a Layer rule.
[4.0.0] -- 2024-04-01
Changed
- Mechanism of exporting library modules
- Python formatting
- Support for python 3.13
Fixed
- KeyError when trying to check for imports of submodule by same submodule
[3.1.1] -- 2024-10-02
Fixed
- Link to documentation
[3.1.0] -- 2024-09-16
Added
- Filter for external dependencies.
[3.0.1] -- 2024-07-25
Fixed
- Regex syntax error in newer python versions
[3.0.0] -- 2024-05-27
Added
- Support for python 3.12
Removed
- Support for python 3.8
[2.0.3] -- 2024-03-17
Fixed
- Module resolution for nested modules with root and module path mismatch.
[2.0.2] -- 2024-03-16
Fixed
- Module resolution for absolute imports when root and module path differ.
[2.0.1] -- 2024-03-14
Fixed
- Readme description on module name references.
[2.0.0] -- 2024-02-23
Changed
- Matplotlib is no longer installed by default, as it is not required for the core functionality.
[1.6.0] -- 2024-02-23
Added
- Support for mypy.
Changed
- Syntax for example rules in documentation.
[1.5.1] -- 2023-11-21
Removed
- Unnecessary dependency.
[1.5.0] -- 2023-04-12
Added
- More extensive documentation for the regex matching features.
[1.4.1] -- 2023-04-09
Fixed
- Import paths in documentation.
[1.4.0] -- 2023-04-09
Added
- Option to specify module names and file exclusions via regex.
- Support for layer architecture.
- Batch support for rule subjects.
Changed
- Complete module hierarchy present in graph, instead of only for modules that are part of an import dependency.
- Error message only lists violated rule objects when multiple rule objects are defined.
[1.3.1] -- 2023-01-27
Changed
- Internal module structure to improve coupling, cohesion.
Fixed
- Support for python3.8.
[1.3.0] -- 2022-11-28
Added
- Reference to modules only by part of their names
- Aliases for plot labels
- Test generation from PUML component diagrams
[1.2.1] -- 2022-10-21
Fixed
- Queries with "except itself" excluded too many modules from consideration
[1.2.0] -- 2022-10-08
Fixed
- Line separator for rule violation messages
[1.1.1] -- 2022-10-04
Fixed
- Line separator for rule violation messages
[1.1.0] -- 2022-10-02
Added
- Rule objects can be passed in as a list
Fixed
- All violations not always returned
[1.0.3] -- 2022-09-28
Changed
- Simplified calculation of whether imports are within the project
Fixed
- Excluded directories no longer searched for parseable files
[1.0.2] -- 2022-09-28
Fixed
- All internal modules filtered out if module and root path are not identical and are a sub module of the actual root module.
[1.0.1] -- 2022-09-27
Fixed
- Module no longer considered if rule asked only for submodules of a given module
[1.0.0] -- 2022-09-27
Changed
- Rename entry point methods for clarity
Fixed
- Reference to outdated code in documentation
[0.1.0] -- 2022-09-23
Added
- More detailed error message for violations of rules checking for absence of imports
- Versioning of documentation
[0.1.0-alpha.4] -- 2022-09-23
Added
- Documentation for release of PyTestArch
- More detailed project description
Changed
- Computation of dependencies between modules on graph
[0.1.0-alpha.3] -- 2022-09-20
Fixed
- Different construction of graph node names between OS
[0.1.0-alpha.1] -- 2022-09-20
Added
- Documentation
- library MVP