You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
from_format raises AttributeError for ordinal tokens (Do in locales without ordinals; Qo/Mo/DDDo/e/eo/do/Wo/wo everywhere) #1074
pendulum.from_format cannot parse any of the ordinal tokens, raising a raw AttributeError instead of a ValueError — and for Do the crash is locale-dependent:
1. Do in locales without ordinal suffixes (bg, de, ru, pl, uk, da, sv, ...) — the crash comes from Formatter._LOCALIZABLE_TOKENS:
>>>pendulum.datetime(1982, 6, 7).format("Do MMMM YYYY", locale="de")
'7 Juni 1982'# format() is fine>>>pendulum.from_format("7 Juni 1982", "Do MMMM YYYY", locale="de")
AttributeError: 'NoneType'objecthasnoattribute'values'
Locale.get("custom.ordinal") returns None for these locales and the Do entry does locale.get("custom.ordinal").values(). Note Locale.ordinalize() (used by format()) already falls back to the plain number when the locale has no ordinals, so the parse pattern should accept plain digits — exactly what format() emits.
2. Qo, Mo, DDDo, do, e, eo, Wo, wo in every locale — these tokens are None in _LOCALIZABLE_TOKENS, so _replace_tokens does locale.translation(None).values():
The AttributeError leaks from a public API instead of the regular ValueError("Unsupported token: ...") that _replace_tokens is designed to raise for tokens without candidates.
Root cause
src/pendulum/formatting/formatter.py:
The Do lambda calls .values() on locale.get("custom.ordinal") without a None guard (line ~79-81).
The None entries for the other ordinal tokens are passed to locale.translation(None) which returns None (lines ~70-77), and .values() crashes. The intended ValueError(f"Unsupported token: {token}") guard at the bottom of _replace_tokens is never reached.
Expected behavior
from_format must accept whatever format() emits: for Do in a locale without ordinals that means plain digits (\d+); for locales with ordinals the existing \d+<suffix> patterns.
Tokens that are deliberately not parse-supported should raise the existing ValueError("Unsupported token: X"), not AttributeError.
Mo, Qo and DDDo are straightforwardly parseable (month / quarter / day of year — _check_parsed already consumes all three fields), so they get real support, giving format/from_format round-trips for them.
Activity