It seems like we're trying to call .join() on a byte string. I suggest just fixing the crash but keeping byte strings disallowed - they won't work with gettext anyway because gettext uses normal strings internally.
_parse_python_string() is annotated as -> str | None, but when it encounters a byte-string literal (e.g. b'foo'), it evaluates to an ast.Constant whose .value is a bytes object and returns bytes unchanged. The caller in extract_python appends this to buf (a list of string fragments), then calls ''.join(buf) — which fails because str.join() requires all items to be str:
Fix: Add an isinstance(body.value, str) guard in _parse_python_string so that byte-string literals are silently skipped (returning None), matching the behaviour for all other non-extractable expressions:
if isinstance(body, ast.Constant):
- return body.value+ if isinstance(body.value, str):+ return body.value
Byte strings cannot be used as gettext messages anyway, so ignoring them is the correct behaviour. The crash is avoided and callers calling _(b'foo') will simply have no entry extracted, with no error.
Activity
akx commented on Mar 3, 2025
Agreed. We should warn about that and leave it non-extracted.
tomasr8 commented on Mar 5, 2025
I'll put it on my todo list for this weekend
Mukller commented on Jul 29, 2026
Root cause and fix:
_parse_python_string() is annotated as -> str | None, but when it encounters a byte-string literal (e.g. b'foo'), it evaluates to an ast.Constant whose .value is a bytes object and returns bytes unchanged. The caller in extract_python appends this to buf (a list of string fragments), then calls ''.join(buf) — which fails because str.join() requires all items to be str:
Fix: Add an isinstance(body.value, str) guard in _parse_python_string so that byte-string literals are silently skipped (returning None), matching the behaviour for all other non-extractable expressions:
Byte strings cannot be used as gettext messages anyway, so ignoring them is the correct behaviour. The crash is avoided and callers calling _(b'foo') will simply have no entry extracted, with no error.
I have a PR ready.