Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bibtexparser/splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ def _is_escaped():
return next_mark.start()
# Check for end of entry:
elif next_mark.group(0) == "}" and not _is_escaped():
# A malformed escaped-brace sequence can reduce the tracked depth too early.
# If a comma follows, this brace belongs to the field value; consume it and let
# the comma terminate the field instead of silently dropping subsequent fields.
remaining = self.bibstr[next_mark.end() :].lstrip()
if remaining.startswith(","):
continue
self._unaccepted_mark = next_mark
return next_mark.start()

Expand Down
24 changes: 24 additions & 0 deletions tests/middleware_tests/test_enclosing.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,28 @@ def test_string_reference_roundtrip():
assert "year = {2019}" in written


def test_escaped_brace_sequence_roundtrip():
"""The issue #452 field value remains parseable after default parse and write middleware."""
bibtex = r"""@Article{Konstadinidis2009,
abstract = {The 396$\{2}$ chip is fabricated in a 65-nm process.},
author_keywords = {Arrays; Chip multi-threading (CMT); Register files}
}"""
expected_abstract = r"The 396$\{2}$ chip is fabricated in a 65-nm process."

library = bibtexparser.parse_string(bibtex)
assert len(library.failed_blocks) == 0
parsed_abstract = library.entries[0]["abstract"]
assert parsed_abstract == expected_abstract

written = bibtexparser.write_string(library)
assert r"396$\{2}$" in written

reparsed = bibtexparser.parse_string(written)
assert len(reparsed.failed_blocks) == 0
assert reparsed.entries[0]["abstract"] == parsed_abstract
assert reparsed.entries[0]["author_keywords"] == (
"Arrays; Chip multi-threading (CMT); Register files"
)


# TODO round-trip tests (removal -> addition -> removal)
Loading