Intro
attrsx – An Extension to attrs for Declarative Handler Injection & Integrated Logging
1. Purpose
attrsx builds on top of the attrs data‑class library and contributes two orthogonal capabilities that are frequently needed in service‑layer and infrastructure code:
- Integrated Logging – transparent, boilerplate‑free creation of a configured
logging.Loggerfor every instance. - Declarative Handler Injection – generation of lazy‑initialised strategy objects (“handlers”) declared via
handler_specs.
Both features are opt‑in; if a class does not request them, attrsx.define behaves exactly like attrs.define.
2. Key Features
-
Drop-in replacement for
attrs.define– forwards every keyword parameter thatattrs.defineunderstands (slots,frozen,kw_only, validators, converters, …). -
Integrated logging – injects four fields (
logger,loggerLvl,logger_name,logger_format) and wraps__attrs_post_init__to attaches alogging.loggerto the instance if none is present. The logger’s name defaults to the class name; level and format are configurable per instance. -
Declarative handler injection – for each entry in
handler_specs={"key": HandlerClass, ...}the decorator adds three fields
(<key>_class,<key>_params,<key>_h) plus a lazy factory method_initialize_<key>_h(), which calls
<key>_class(**<key>_params)the first time it is invoked. -
Type-hint compliance – all generated attributes are inserted into
__annotations__, so static type-checkers recognise them. -
Zero runtime overhead – field and method generation is performed once, at import time; normal instance construction stays on the
attrsfast path.
3. Underlying Pattern
The handler mechanism realises Declarative Handler Injection (a variant of the Strategy pattern implemented through composition and a lazy factory method). Configuration is expressed as data, leaving the host class agnostic of concrete strategy classes.
4. When to Use
- You need consistent, ready‑to‑use logging across many small classes.
- You want to supply interchangeable helper or strategy objects without manual glue code.
- You prefer composition over inheritance but dislike factory boilerplate.
If none of the above apply, simply continue to use attrs.define; the migration path is one import statement.
Installation
pip install attrsx