diff --git a/msgspec/_core.c b/msgspec/_core.c index 5e9804c7..022b2e1c 100644 --- a/msgspec/_core.c +++ b/msgspec/_core.c @@ -5140,17 +5140,23 @@ rename_camel_inner(PyObject *field, bool cap_first) { bool first = true; for (Py_ssize_t i = 0; i < PyList_GET_SIZE(parts); i++) { PyObject *part = PyList_GET_ITEM(parts, i); - if (PyUnicode_GET_LENGTH(part) == 0) continue; - if (!first || cap_first) { - /* convert part to title case, inplace in the list */ - PyObject *part_title = PyObject_CallMethod(part, "title", NULL); - if (part_title == NULL) goto cleanup; - PyList_SET_ITEM(parts, i, part_title); + if (first && (PyUnicode_GET_LENGTH(part) == 0)) { + /* Preserve leading underscores */ + PyList_SET_ITEM(parts, i, underscore); Py_DECREF(part); } - first = false; + else { + if (!first || cap_first) { + /* convert part to title case, inplace in the list */ + PyObject *part_title = PyObject_CallMethod(part, "title", NULL); + if (part_title == NULL) goto cleanup; + PyList_SET_ITEM(parts, i, part_title); + Py_DECREF(part); + } + first = false; + } } - empty = PyUnicode_FromString(""); + empty = PyUnicode_FromStringAndSize("", 0); if (empty == NULL) goto cleanup; out = PyUnicode_Join(empty, parts); diff --git a/tests/test_struct.py b/tests/test_struct.py index f3c64076..300ec428 100644 --- a/tests/test_struct.py +++ b/tests/test_struct.py @@ -1942,12 +1942,14 @@ class Test(Struct, rename="kebab"): field_two_with_suffix: str __field_three__: bool field4: float + _field_five: int assert Test.__struct_encode_fields__ == ( "field-one", "field-two-with-suffix", "field-three", "field4", + "field-five", ) def test_rename_camel(self): @@ -1956,12 +1958,14 @@ class Test(Struct, rename="camel"): field_two_with_suffix: str __field__three__: bool field4: float + _field_five: int assert Test.__struct_encode_fields__ == ( "fieldOne", "fieldTwoWithSuffix", - "fieldThree", + "__fieldThree", "field4", + "_fieldFive", ) def test_rename_pascal(self): @@ -1970,12 +1974,14 @@ class Test(Struct, rename="pascal"): field_two_with_suffix: str __field__three__: bool field4: float + _field_five: int assert Test.__struct_encode_fields__ == ( "FieldOne", "FieldTwoWithSuffix", - "FieldThree", + "__FieldThree", "Field4", + "_FieldFive", ) def test_rename_callable(self):