diff --git a/python/msgspec_toon/__init__.py b/python/msgspec_toon/__init__.py index c1e732c..0c2d0cb 100644 --- a/python/msgspec_toon/__init__.py +++ b/python/msgspec_toon/__init__.py @@ -37,6 +37,18 @@ EncodeError: Final = msgspec.EncodeError +_NON_STRING_KEY_ERROR: Final = ( + "object keys must be strings; convert with msgspec.to_builtins(..., str_keys=True)" +) + + +def _new_encode_error(message: str) -> msgspec.EncodeError: + """Translate stable native error codes without changing native hot-path layout.""" + if message == "object keys must be strings": + message = _NON_STRING_KEY_ERROR + return msgspec.EncodeError(message) + + # Stable type identities and the exact-pinned msgspec datetime normalizer. _NATIVE_SCALAR_TYPES: Final = ( datetime.datetime, @@ -271,7 +283,7 @@ def __init__( enc_hook=native_hook, plan_source=encode_plan_for, struct_base=msgspec.Struct, - encode_error=msgspec.EncodeError, + encode_error=_new_encode_error, delimiter=delimiter, indent=indent, ) diff --git a/src/encode.rs b/src/encode.rs index 3cfc3f7..9f3f2d6 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -26,8 +26,6 @@ use crate::writer::Writer; const CELL_WIDTH_ESTIMATE: usize = 10; const MAX_HOOK_DEPTH: usize = 8; -const NON_STRING_KEY_ERROR: &str = - "object keys must be strings; convert with msgspec.to_builtins(..., str_keys=True)"; pub struct EncodeContext { pub enc_hook: Option>, @@ -406,7 +404,7 @@ fn object_pairs<'value, 'py>( let mut pairs = Vec::with_capacity(map.len()); for (key, item) in map.iter() { let Ok(key_text) = key.cast_into::() else { - return Err(encode_err(ctx, py, NON_STRING_KEY_ERROR)); + return Err(encode_err(ctx, py, "object keys must be strings")); }; pairs.push((EntryText::Object(key_text), item)); } @@ -781,7 +779,7 @@ fn root_object_decision<'value, 'py>( let mut rows = (map.len() >= 2).then(|| Vec::with_capacity(map.len())); for (key, item) in map.iter() { let Ok(key_text) = key.cast_into::() else { - return Err(encode_err(ctx, py, NON_STRING_KEY_ERROR)); + return Err(encode_err(ctx, py, "object keys must be strings")); }; if let Some(candidate_rows) = rows.as_mut() { if item.is_instance_of::() || item.is_instance(ctx.struct_base.bind(py))? {