Repository: texworld/betterbib (language: python)

Issue:
Conserve `preamble` declarations
As `@preamble` declarations are officially supported by `bibtex` they should not be dropped when reformatting the file.

(already on implementing it)


Discussion / hints:

Fix patch:
diff --git a/src/betterbib/cli/_format.py b/src/betterbib/cli/_format.py
index cdbbe78..15757b5 100644
--- a/src/betterbib/cli/_format.py
+++ b/src/betterbib/cli/_format.py
@@ -18,7 +18,13 @@ def run(args):
 
         d = dict(tuples)
         d = adapt_doi_urls(d, args.doi_url_type)
-        string = to_string(d, args.delimiter_type, tab_indent=args.tab_indent)
+        string = to_string(
+            d,
+            args.delimiter_type,
+            tab_indent=args.tab_indent,
+            # FIXME: use public argument when it becomes possible
+            preamble=data._preamble,
+        )
 
         write(string, infile if args.in_place else None)
 
diff --git a/src/betterbib/tools.py b/src/betterbib/tools.py
index 0c76841..f3737e6 100644
--- a/src/betterbib/tools.py
+++ b/src/betterbib/tools.py
@@ -410,12 +410,31 @@ def heuristic_unique_result(results, d):
 
 # This used to be a write() function, but beware of exceptions! Files would get
 # unintentionally overridden, see <https://github.com/nschloe/betterbib/issues/184>
-def to_string(od, delimiter_type: str, tab_indent: bool):
+def to_string(od, delimiter_type: str, tab_indent: bool, preamble: list = []):
+    """
+    Creates a string representing the bib entries
+
+        Parameters:
+            od (dict): dictionary of bibtex entries
+            outfile (FileType("r")): file to write to (default: None)
+            delimiter_type (str): delimiter to use to mark strings
+            tab_indent (bool): whether to use tabs to indent the entries
+            preamble (list): list of preamble commands
+
+        Returns:
+            string
+    """
     # Write header to the output file.
     segments = [f"%comment{{This file was created with betterbib v{__version__}.}}\n"]
 
     delimiters = {"braces": ("{", "}"), "quotes": ('"', '"')}[delimiter_type]
 
+    if preamble:
+        # Add segments for each preamble entry
+        segments.extend(
+            [f'@preamble{{"{preamble_string}"}}' for preamble_string in preamble]
+        )
+
     # Add segments for each bibtex entry in order
     segments.extend(
         [


Test patch:
diff --git a/tests/cli/test_format_cli.py b/tests/cli/test_format_cli.py
index 875312b..040db2f 100644
--- a/tests/cli/test_format_cli.py
+++ b/tests/cli/test_format_cli.py
@@ -10,6 +10,38 @@
 )
 
 
+TEST_BIBTEXT_PREAMBLE_UNFORMATTED = (
+    '@preamble{"\\RequirePackage{biblatex}"}\n'
+    '@preamble{"\\addbibressource{dependend.bib}"}\n'
+    "@article{foobar,\n"
+    "doi={foobar},\n"
+    "url = {https://doi.org/foobar}\n"
+    "}"
+)
+
+TEST_BIBTEXT_PREAMBLE_FORMATTED_KEEP = (
+    version_line + "\n"
+    "\n"
+    '@preamble{"\\RequirePackage{biblatex}"}\n'
+    "\n"
+    '@preamble{"\\addbibressource{dependend.bib}"}\n'
+    "\n"
+    "@article{foobar,\n"
+    " doi = {foobar},\n"
+    " url = {https://doi.org/foobar},\n"
+    "}\n"
+)
+
+TEST_BIBTEXT_PREAMBLE_FORMATTED_DROP = (
+    version_line + "\n"
+    "\n"
+    "@article{foobar,\n"
+    " doi = {foobar},\n"
+    " url = {https://doi.org/foobar},\n"
+    "}\n"
+)
+
+
 @pytest.mark.parametrize(
     "ref_in,ref_out",
     [
@@ -70,6 +102,21 @@
             r" title = {Foo \& Bar on \LaTeX @TheBridge}," + "\n"
             "}\n",
         ),
+        # Keeping when unformatted
+        (
+            TEST_BIBTEXT_PREAMBLE_UNFORMATTED,
+            TEST_BIBTEXT_PREAMBLE_FORMATTED_KEEP,
+        ),
+        # Keeping when preamble and preformatted
+        (
+            TEST_BIBTEXT_PREAMBLE_FORMATTED_KEEP,
+            TEST_BIBTEXT_PREAMBLE_FORMATTED_KEEP,
+        ),
+        # Keeping when no preamble and preformatted
+        (
+            TEST_BIBTEXT_PREAMBLE_FORMATTED_DROP,
+            TEST_BIBTEXT_PREAMBLE_FORMATTED_DROP,
+        ),
     ],
 )
 def test_cli_format(ref_in, ref_out, capsys):