Parent
#176
What to build
Extend _resolve_effective_nulls in utils/_null_normalization.py to accept an optional string_sentinels: dict[str, list[str]] | None parameter (default None).
For each String/Utf8 column in the DataFrame:
- If the column name is present in
string_sentinels, match only the declared values (case-insensitive via .str.to_uppercase()). The hardcoded _SENTINEL_STRINGS set is not applied for that column.
- If the column name is absent, fall back to the existing
_SENTINEL_STRINGS behaviour (no change).
- Empty/whitespace-only string detection is always applied unconditionally, regardless of any declaration.
The function remains pure and side-effect-free. When string_sentinels is None or {}, behaviour is strictly identical to the current implementation — no existing caller is broken.
Acceptance criteria
Blocked by
Parent
#176
What to build
Extend
_resolve_effective_nullsinutils/_null_normalization.pyto accept an optionalstring_sentinels: dict[str, list[str]] | Noneparameter (defaultNone).For each String/Utf8 column in the DataFrame:
string_sentinels, match only the declared values (case-insensitive via.str.to_uppercase()). The hardcoded_SENTINEL_STRINGSset is not applied for that column._SENTINEL_STRINGSbehaviour (no change).The function remains pure and side-effect-free. When
string_sentinelsisNoneor{}, behaviour is strictly identical to the current implementation — no existing caller is broken.Acceptance criteria
_resolve_effective_nullssignature gainsstring_sentinels: dict[str, list[str]] | None = None_SENTINEL_STRINGSapplies (regression: current behaviour preserved)"?") is not convertedstring_sentinelsbut absent from the DataFrame → no error, rest of DataFrame unchangedstring_sentinels=None→ output byte-for-byte identical to calling with no argumenttests/unit/imputation/test_null_normalization.pyBlocked by