Skip to content

Commit 8152ff7

Browse files
committed
Preserve trailing backslashes in name round trips
* Avoid duplicating a terminal backslash while splitting names. * Escape comma-adjacent name sections without changing the final section. * Enable the strict and non-strict inverse regressions.
1 parent 08e370a commit 8152ff7

2 files changed

Lines changed: 9 additions & 20 deletions

File tree

bibtexparser/middlewares/names.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,14 @@ def escape_last_slash(string: str) -> str:
143143
last = " ".join(self.last) if self.last else None
144144
jr = " ".join(self.jr) if self.jr else None
145145

146-
von_last = " ".join(name for name in [von, last] if name)
147-
return ", ".join(escape_last_slash(name) for name in [von_last, jr, first] if name)
146+
sections = [
147+
name for name in [" ".join(name for name in [von, last] if name), jr, first] if name
148+
]
149+
150+
# Only sections followed by a comma need protection from a trailing backslash.
151+
# Escaping the final section changes its value even though no delimiter follows it.
152+
escaped_sections = [escape_last_slash(section) for section in sections[:-1]]
153+
return ", ".join([*escaped_sections, sections[-1]])
148154

149155

150156
class SplitNameParts(_NameTransformerMiddleware):
@@ -311,6 +317,7 @@ def parse_single_name_into_parts(name: str, strict: bool = True) -> NameParts:
311317
# If we're at the end of the string, then the \ is just a \.
312318
except StopIteration:
313319
word.append(char)
320+
continue
314321

315322
# Start of a braced expression.
316323
if char == "{":

tests/middleware_tests/test_names.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -875,26 +875,8 @@ def test_split_name_into_parts(name, expected_as_dict, strict):
875875
def test_merge_last_name_first_inverse(name, expected_as_dict, strict):
876876
"""Tests that merging name parts using the last-name-first method
877877
maintains the "semantics" of the name.
878-
879-
This property does not hold for certain values that contain '\\'.
880878
"""
881879

882-
# cases where either the last name or "von" part ends with an odd number of `\` cannot be handled,
883-
# since in those cases the `,` is escaped when the name parts are put back together
884-
def ends_with_odd_slash(names: list[str]) -> bool:
885-
if len(names) == 0:
886-
return False
887-
name = names[-1]
888-
count = 0
889-
i = len(name) - 1
890-
while i >= 0 and name[i] == "\\":
891-
count += 1
892-
i -= 1
893-
return count % 2 == 1
894-
895-
if any(ends_with_odd_slash(name) for name in expected_as_dict.values()):
896-
pytest.skip("Inverse property does not hold for names ending with '\\'")
897-
898880
nameparts = _dict_to_nameparts(expected_as_dict)
899881
merged = nameparts.merge_last_name_first
900882
resplit = parse_single_name_into_parts(merged, strict=strict)

0 commit comments

Comments
 (0)