From 7b9668411484d56e9f5d1ff9ceefce348c48e15e Mon Sep 17 00:00:00 2001 From: Claudius Ellsel Date: Thu, 16 Jul 2026 16:48:30 +0200 Subject: [PATCH] Make string reference expansion opt-in * Retain references to string definitions in the default parse stack. * Document and test explicit interpolation before enclosure removal. * Preserve macro linkage during ordinary parse/write use. --- bibtexparser/middlewares/parsestack.py | 2 -- docs/source/customize.rst | 15 +++++++++++++- tests/middleware_tests/test_interpolate.py | 23 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/bibtexparser/middlewares/parsestack.py b/bibtexparser/middlewares/parsestack.py index 8bbe789c..5d7c379c 100644 --- a/bibtexparser/middlewares/parsestack.py +++ b/bibtexparser/middlewares/parsestack.py @@ -1,4 +1,3 @@ -from bibtexparser.middlewares import ResolveStringReferencesMiddleware from bibtexparser.middlewares.enclosing import AddEnclosingMiddleware from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware @@ -8,7 +7,6 @@ def default_parse_stack(allow_inplace_modification: bool = True) -> list[Middleware]: """The default parse stack to be applied after splitting, if not specified otherwise.""" return [ - ResolveStringReferencesMiddleware(allow_inplace_modification=allow_inplace_modification), RemoveEnclosingMiddleware(allow_inplace_modification=allow_inplace_modification), ] diff --git a/docs/source/customize.rst b/docs/source/customize.rst index 9b8a2ae4..94af94ef 100644 --- a/docs/source/customize.rst +++ b/docs/source/customize.rst @@ -48,9 +48,22 @@ part of the expected functionality for most users. Currently, the default parse stack consists of the following layers: -* :class:`bibtexparser.middlewares.ResolveStringReferencesMiddleware`: De-Reference reference to @string definitions. * :class:`bibtexparser.middlewares.RemoveEnclosingMiddleware`: Removes enclosing (e.g. curly braces or "") from values. +String references are retained by default because replacing a reference with +its current value destroys the relationship with its ``@string`` definition. +Applications that intentionally want dereferenced values can select +:class:`bibtexparser.middlewares.ResolveStringReferencesMiddleware` explicitly, +before removing enclosures: + +.. code-block:: python + + layers = [ + m.ResolveStringReferencesMiddleware(), + m.RemoveEnclosingMiddleware(), + ] + library = bibtexparser.parse_file("bibtex.bib", parse_stack=layers) + The default write stack consists of the following layers: * :class:`bibtexparser.middlewares.AddEnclosingMiddleware`: Encloses values in curly braces where needed. diff --git a/tests/middleware_tests/test_interpolate.py b/tests/middleware_tests/test_interpolate.py index 264373c9..cc858108 100644 --- a/tests/middleware_tests/test_interpolate.py +++ b/tests/middleware_tests/test_interpolate.py @@ -1,5 +1,6 @@ import pytest +import bibtexparser from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware from bibtexparser.middlewares.interpolate import ResolveStringReferencesMiddleware from bibtexparser.splitter import Splitter @@ -18,6 +19,28 @@ """ +def test_default_parse_retains_string_reference_linkage(): + """Ordinary parse/write use must not replace a reusable macro with its current value.""" + library = bibtexparser.parse_string(bibtex_string) + + assert library.entries_dict["test_article"]["note"] == "test_note" + written = bibtexparser.write_string(library) + assert "note = test_note" in written + + +def test_public_parse_can_expand_string_references_explicitly(): + """Callers can request dereferencing before the enclosing representation changes.""" + library = bibtexparser.parse_string( + bibtex_string, + parse_stack=[ + ResolveStringReferencesMiddleware(), + RemoveEnclosingMiddleware(), + ], + ) + + assert library.entries_dict["test_article"]["note"] == "This is a test note." + + def test_string_interpolation_middleware_interpolates_string(): original_lib = Splitter(bibtex_string).split()