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.Logger
for 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.define
understands (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.logger
to 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
attrs
fast 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