Summary
The builder validation system supports type checking and validators (Regex, Range) via Annotated metadata, but has no way to transform values before validation. This is needed for polymorphic type acceptance — e.g., date fields that should accept both datetime.date objects and ISO date strings, normalizing to date.
Proposal
Add a Coerce base class alongside existing validators:
from typing import Annotated
from datetime import date
@element(sub_tags='')
def evento(self, data: Annotated[date, DateCoerce()] = None): ...
# All work:
bag.evento(data=date(2024, 1, 15)) # date passthrough
bag.evento(data='2024-01-15') # str → date coercion
bag.evento(data='2024-01-15::D') # typed string → date coercion
How it works
Coerce subclasses are frozen dataclasses with __call__ (same pattern as Regex/Range)
- Unlike validators (raise on failure, return None), coercers return the transformed value
- They live in the same
Annotated metadata list — no change to the (base_type, validators, default) tuple format
_validate_call_args partitions metadata into coercers (isinstance(m, Coerce)) and validators at runtime
- Execution order: coercion → type check → validation
- Coerced values are written back to the
attr dict, propagating to the Bag node
Changes needed in builder.py
- Add
Coerce base class after Range (~line 414)
- Modify
_validate_call_args to partition metadata and run coercers before type-check
- One-line fix in wrapper to propagate coerced
node_value
- Export
Coerce from __init__.py
Motivation
ERPY builders have ~10 date fields across erpy_db_op_builder.py, mag_builder.py, and coge_builder.py currently typed as str. Users want to pass Python date objects (preferred) or ISO strings interchangeably. The same pattern applies to Decimal, int, bool, time, datetime fields.
Notes
- 100% backward compatible — without coercers in metadata, behavior is identical
- Generalizable: concrete coercer implementations can live outside genro-builders (e.g., in ERPY using GnrClassCatalog)
- The
_split_annotated function already extracts all callables from Annotated — no change needed there
Summary
The builder validation system supports type checking and validators (
Regex,Range) viaAnnotatedmetadata, but has no way to transform values before validation. This is needed for polymorphic type acceptance — e.g., date fields that should accept bothdatetime.dateobjects and ISO date strings, normalizing todate.Proposal
Add a
Coercebase class alongside existing validators:How it works
Coercesubclasses are frozen dataclasses with__call__(same pattern asRegex/Range)Annotatedmetadata list — no change to the(base_type, validators, default)tuple format_validate_call_argspartitions metadata into coercers (isinstance(m, Coerce)) and validators at runtimeattrdict, propagating to the Bag nodeChanges needed in builder.py
Coercebase class afterRange(~line 414)_validate_call_argsto partition metadata and run coercers before type-checknode_valueCoercefrom__init__.pyMotivation
ERPY builders have ~10 date fields across
erpy_db_op_builder.py,mag_builder.py, andcoge_builder.pycurrently typed asstr. Users want to pass Pythondateobjects (preferred) or ISO strings interchangeably. The same pattern applies toDecimal,int,bool,time,datetimefields.Notes
_split_annotatedfunction already extracts all callables fromAnnotated— no change needed there