Purpose¶
An errors module reads best when its filename names exactly the exception it raises: order_not_found_error.py holds OrderNotFoundError and nothing else. This recipe caps every module under an errors/ directory at a single top-level class.
It pairs with One Class Per Module. That rule already bounds every domain module at one class, and errors/ sits inside domain/**, so error files are covered twice. The point is the narrower, explicitly named rule: the violation is reported as one-error-per-file rather than the generic domain cap, and the guarantee survives even if you later relax or scope-exclude the broad rule for some other module.
Configuration¶
rules:
- name: one-error-per-file
type: symbol-count
description: Each errors module defines a single error class
within: src/contexts/{ctx}/domain/**/errors/**
symbol: class
max: 1
ignore_files: [__init__.py]
within matches every .py file under an errors directory in any context's domain — the ** on either side lets errors sit at any depth, whether directly under domain or nested beneath a subdomain. symbol: class counts top-level class definitions; max: 1 caps each module at one; ignore_files skips __init__.py, which re-exports errors and defines no class of its own.
Violation Example¶
The orders context bundles two error types into one module:
# src/contexts/orders/domain/errors/order_errors.py
class OrderNotFoundError(Exception):
"""Raised when an order id has no matching order."""
class OrderAlreadyPaidError(Exception): # a second error class in the same module
"""Raised when paying an order that is already settled."""
Passing Example¶
Give each error its own module, one class apiece:
src/contexts/orders/domain/errors/
├── __init__.py
├── order_not_found_error.py # class OrderNotFoundError
└── order_already_paid_error.py # class OrderAlreadyPaidError
Output¶
The line points at the surplus symbol — the second class that pushes the count past the limit:
$ psl check
src/contexts/orders/domain/errors/order_errors.py:7
[one-error-per-file] Each errors module defines a single error class
2 top-level class definitions found, at most 1 allowed
Found 1 violation(s).