From 53159739a76cdc9f9237b84a7858cce2de8900a5 Mon Sep 17 00:00:00 2001 From: Michael Weiss Date: Fri, 12 Jun 2026 13:06:40 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Resolve=20@string=20references?= =?UTF-8?q?=20case-insensitively?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BibTeX treats string (macro) keys as case-insensitive, but ResolveStringReferencesMiddleware did an exact dict lookup, so e.g. `month = JAN` did not resolve against `@string{jan = ...}`. Build a casefolded lookup once per transform; on keys differing only in case, the later definition wins, matching BibTeX redefinition semantics. Fixes #541 Co-Authored-By: Claude Fable 5 --- bibtexparser/middlewares/interpolate.py | 8 ++++++-- tests/middleware_tests/test_interpolate.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bibtexparser/middlewares/interpolate.py b/bibtexparser/middlewares/interpolate.py index de171bd..2e5d832 100644 --- a/bibtexparser/middlewares/interpolate.py +++ b/bibtexparser/middlewares/interpolate.py @@ -38,6 +38,9 @@ def transform(self, library: Library) -> Library: if not self.allow_inplace_modification: library = deepcopy(library) + # BibTeX string keys are case-insensitive; later definitions win. + string_values = {key.lower(): s.value for key, s in library.strings_dict.items()} + entry: Entry raised_enclosing_warning = False for entry in library.entries: @@ -58,9 +61,10 @@ def transform(self, library: Library) -> Library: for field in entry.fields: if _value_is_nonstring_or_enclosed(field.value): continue - if field.value not in library.strings_dict: + try: + field.value = string_values[field.value.lower()] + except KeyError: continue - field.value = library.strings_dict[field.value].value resolved_fields.append(field.key) if resolved_fields: diff --git a/tests/middleware_tests/test_interpolate.py b/tests/middleware_tests/test_interpolate.py index 1262fa3..264373c 100644 --- a/tests/middleware_tests/test_interpolate.py +++ b/tests/middleware_tests/test_interpolate.py @@ -35,6 +35,26 @@ def test_string_interpolation_middleware_interpolates_string(): ) +def test_string_interpolation_is_case_insensitive(): + bibtex = """ + @string{lowercase = "Lower Case Note."} + @string{UPPERCASE = "Upper Case Note."} + + @article{test_article, + note = LOWERCASE, + comment = uppercase + } + """ + library = Splitter(bibtex).split() + + m = ResolveStringReferencesMiddleware() + library = m.transform(library) + + fields = library.entries_dict["test_article"].fields_dict + assert fields["note"].value == '"Lower Case Note."' + assert fields["comment"].value == '"Upper Case Note."' + + def test_warning_is_raised_if_enclosings_are_removed(): original_lib = Splitter(bibtex_string).split() m = RemoveEnclosingMiddleware(allow_inplace_modification=False)