Skip to content

Commit

Permalink
Preserve leading _ for camel/pascal renaming
Browse files Browse the repository at this point in the history
Previously leading underscores were stripped when renaming fields to a
`camelCase` or `PascalCase` convention. We now preserve leading
underscores, better matching the behavior and conventions of other
libraries.

Before: `_field_one` -> `fieldOne`
Now: `_field_one` -> `_fieldOne`
  • Loading branch information
jcrist committed Dec 20, 2023
1 parent 9fb503a commit f2cc0e5
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 10 deletions.
22 changes: 14 additions & 8 deletions msgspec/_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 8 additions & 2 deletions tests/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down

0 comments on commit f2cc0e5

Please sign in to comment.