Attribute Enricher
Rule-based attribute enrichment for any sequence of items.
Overview
vcti-attribute-enricher adds attributes to items in a sequence by
matching vcti-lookup rules. Each EnrichRule pairs a condition
(when) with attributes to write (set); the enricher walks an
iterable, evaluates each rule against each item, and stamps matching
items with the rule's attributes.
The package is iterable-generic, not tree-specific: the items can be
tree-node payloads (e.g., from vcti-fileloader), dicts, dataclasses,
or any object with an attribute-bearing surface. Reading and writing
are pluggable via getter / setter callables, with sensible defaults
for vcti-fileloader's DataNode-shaped payloads.
The package is the write-side counterpart to vcti-lookup: where
Lookup filters a sequence by rules, the enricher mutates the sequence
by rules.
Installation
pip install vcti-attribute-enricher
In requirements.txt
vcti-attribute-enricher>=1.0.0
In pyproject.toml dependencies
dependencies = [
"vcti-attribute-enricher>=1.0.0",
]
Quick Start
from vcti.attribute_enricher import EnrichRule, apply_rules
from vcti.lookup import MISSING, Rule
items = [
{"name": "stress.h5", "dtype": "float64", "attributes": {}, "enricher_attributes": {}},
{"name": "ids.h5", "dtype": "int64", "attributes": {}, "enricher_attributes": {}},
{"name": "config.json", "dtype": "object", "attributes": {}, "enricher_attributes": {}},
]
def getter(item, key):
# Read from the item's "attributes" dict, then the top-level item.
# Return MISSING (not None) when absent so the rule cleanly no-matches.
if key in item["attributes"]:
return item["attributes"][key]
return item.get(key, MISSING)
apply_rules(
items,
rules=[
EnrichRule(set={"loaded_at": "2026-06-06"}), # every item
EnrichRule(set={"category": "mechanical"},
when=(Rule("name", "^=", "stress"),)), # name starts with "stress"
EnrichRule(set={"is_numeric": True},
when=(Rule("dtype", "^=", "float"),
Rule("dtype", "!=", "object"))), # AND across rules
],
getter=getter,
setter=lambda item, key, value: item["enricher_attributes"].__setitem__(key, value),
)
For payloads with a single mutable attribute dict (the common case):
items = [{"name": "x", "tags": {}}, {"name": "y", "tags": {}}]
apply_rules(
items,
rules=[EnrichRule(set={"seen": True})],
getter=lambda item, key: item.get(key, MISSING),
setter=lambda item, key, value: item["tags"].__setitem__(key, value),
)
Getters must return
vcti.lookup.MISSINGfor absent attributes, notNone.Noneis a legal value that gets passed to the operator;MISSINGshort-circuits the rule to "no match".
Enriching tree-node payloads
The package's default getter/setter are tuned for vcti-fileloader's
DataNode/LazyDataNode payloads, which carry both a read-only
file_attributes mapping (file-native) and a mutable
enricher_attributes dict. The merged read view is exposed via
.attributes (a ChainMap).
from vcti.attribute_enricher import EnrichRule, apply_rules
from vcti.tree import descendants
from vcti.lookup import Rule
apply_rules(
descendants(tree, subtree_root, include_self=True),
rules=[
EnrichRule(set={"file_path": str(path)}),
EnrichRule(set={"category": "mechanical"},
when=(Rule("name", "^=", "stress"),)),
],
)
No getter / setter arguments needed — the defaults read
item.attributes (ChainMap) and write to item.enricher_attributes.
How it works
- Iterate the supplied items.
- For each item, evaluate every
EnrichRule:- If
whenis empty, the rule matches. - If
whenhas one or moreRules, every rule must match (AND). - A rule matches by reading the relevant attribute through
getterand evaluating it viavcti.predicate.evaluate(forwarded throughvcti-lookup).
- If
- On a match, write each key/value in
setto the item viasetter.
Layering — last write wins. Multiple EnrichRules that match the
same item all apply, in the order given. Later rules overwrite earlier
ones on collision. This makes it easy to express "set a default for
everything, then refine for specific subsets."
Return value. apply_rules returns an EnrichResult with summary
counts — handy for logging and for spotting dead rules:
result = apply_rules(items, rules)
print(result.items_visited, result.items_matched, result.writes_applied)
print(result.per_rule_matches) # match count per rule, in order; 0 = dead rule
The items are mutated in place; the result carries metrics only.
API
| Symbol | Description |
|---|---|
EnrichRule(set, when=()) |
Frozen dataclass. set is a dict of attributes to write; when is a tuple[Rule, ...] combined with AND logic. Empty when matches every item. Not hashable (the set dict). |
apply_rules(items, rules, *, getter=None, setter=None) |
Walk items, evaluate rules, stamp matches. Returns an EnrichResult. Defaults to vcti.lookup.attributes_getter and a setter that writes to item.enricher_attributes. Mutates in place (not thread-safe); raises ValueError with rule context if evaluation fails (fail-fast, non-transactional). |
EnrichResult(items_visited, items_matched, writes_applied, per_rule_matches) |
Frozen dataclass of summary counts returned by apply_rules. per_rule_matches is per-rule, in order (a 0 flags a dead rule). |
Getter / Setter |
Type aliases for the (item, key) -> value and (item, key, value) -> None callables. |
Values in set are written by reference — a mutable value shared across
matched items is the same object on each. See docs/patterns.md
for pitfalls.
Dependencies
- vcti-lookup (>=1.0.0) —
Rule,MISSING, defaultattributes_getter - vcti-predicate (>=1.0.0) —
evaluate()is called directly for rule matching
Metadata
Release files for vcti-attribute-enricher 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vcti_attribute_enricher-1.0.0.tar.gz | 15.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vcti_attribute_enricher-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.9 kB
Release files / vcti_attribute_enricher-1.0.0.tar.gz
| Download URL | vcti_attribute_enricher-1.0.0.tar.gz |
|---|---|
| Size | 15.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc8e5a31d89fd8ce7d397724e62006dee754abf7a8b85af1cc48b7a2500430af
|
|
BLAKE2b-256 checksum How to use checksums |
8cb276a31ef6d07fc85609f8bbfec9f641095019e9f0c040dcbd1c6e54f5b5be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.
Transparency logRelease files / vcti_attribute_enricher-1.0.0-py3-none-any.whl
| Download URL | vcti_attribute_enricher-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f53287c01013bcb8004789bb3d60d9ab26017cab1d08137baec524d7f45b56ee
|
|
BLAKE2b-256 checksum How to use checksums |
23abc39d4163e18ce45a28ddaf61ca77f213519f34d4f116a73060b35fe643f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.
Transparency log