Skip to content
Merged
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
67 changes: 66 additions & 1 deletion bibtexparser/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,52 @@ def _build_unparse_stack(
return list(prepend_middleware) + list(unparse_stack)


def _handle_deprecated_write_params(
unparse_stack: Optional[Iterable[Middleware]],
prepend_middleware: Optional[Iterable[Middleware]],
kwargs: dict,
function_name: str,
) -> tuple[Optional[Iterable[Middleware]], Optional[Iterable[Middleware]]]:
"""Handle deprecated parameter names for write functions.

:param unparse_stack: Current unparse_stack value
:param prepend_middleware: Current prepend_middleware value
:param kwargs: Dictionary of keyword arguments to check for deprecated params
:param function_name: Name of the calling function (for error messages)
:return: Tuple of (unparse_stack, prepend_middleware) with deprecated values migrated
"""
if "parse_stack" in kwargs:
warnings.warn(
"Parameter 'parse_stack' is deprecated. Use 'unparse_stack' instead.",
DeprecationWarning,
stacklevel=3,
)
if unparse_stack is not None:
raise ValueError(
"Cannot provide both 'parse_stack' (deprecated) and 'unparse_stack'. "
"Use 'unparse_stack' instead."
)
unparse_stack = kwargs.pop("parse_stack")

if "append_middleware" in kwargs:
warnings.warn(
"Parameter 'append_middleware' is deprecated. Use 'prepend_middleware' instead.",
DeprecationWarning,
stacklevel=3,
)
if prepend_middleware is not None:
raise ValueError(
"Cannot provide both 'append_middleware' (deprecated) and 'prepend_middleware'. "
"Use 'prepend_middleware' instead."
)
prepend_middleware = kwargs.pop("append_middleware")

if kwargs:
raise TypeError(f"{function_name}() got unexpected keyword arguments: {', '.join(kwargs)}")

return unparse_stack, prepend_middleware


def parse_string(
bibtex_str: str,
parse_stack: Optional[Iterable[Middleware]] = None,
Expand Down Expand Up @@ -147,6 +193,7 @@ def write_file(
prepend_middleware: Optional[Iterable[Middleware]] = None,
bibtex_format: Optional[BibtexFormat] = None,
encoding: str = "UTF-8",
**kwargs,
) -> None:
"""Write a BibTeX database to a file.

Expand All @@ -157,7 +204,16 @@ def write_file(
:param prepend_middleware: List of middleware to prepend to the default stack.
Only applicable if `unparse_stack` is None.
:param bibtex_format: Customized BibTeX format to use (optional).
:param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``."""
:param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``.

.. deprecated:: (next version)
Parameters 'parse_stack' and 'append_middleware' are deprecated, will be deleted soon.
Use 'unparse_stack' and 'prepend_middleware' instead.
"""
unparse_stack, prepend_middleware = _handle_deprecated_write_params(
unparse_stack, prepend_middleware, kwargs, "write_file"
)

bibtex_str = write_string(
library=library,
unparse_stack=unparse_stack,
Expand All @@ -176,6 +232,7 @@ def write_string(
unparse_stack: Optional[Iterable[Middleware]] = None,
prepend_middleware: Optional[Iterable[Middleware]] = None,
bibtex_format: Optional["BibtexFormat"] = None,
**kwargs,
) -> str:
"""Serialize a BibTeX database to a string.

Expand All @@ -185,7 +242,15 @@ def write_string(
:param prepend_middleware: List of middleware to prepend to the default stack.
Only applicable if `unparse_stack` is None.
:param bibtex_format: Customized BibTeX format to use (optional).

.. deprecated:: (next version)
Parameters 'parse_stack' and 'append_middleware' are deprecated.
Use 'unparse_stack' and 'prepend_middleware' instead.
"""
unparse_stack, prepend_middleware = _handle_deprecated_write_params(
unparse_stack, prepend_middleware, kwargs, "write_string"
)

middleware: Middleware
for middleware in _build_unparse_stack(unparse_stack, prepend_middleware):
library = middleware.transform(library=library)
Expand Down
151 changes: 151 additions & 0 deletions tests/test_entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import os
import tempfile
import warnings

import pytest

from bibtexparser import parse_file
from bibtexparser import write_file
from bibtexparser import write_string
from bibtexparser.library import Library
from bibtexparser.model import Entry
from bibtexparser.model import Field
Expand Down Expand Up @@ -90,3 +94,150 @@ def test_write_file_roundtrip_gbk():
assert library2.entries[0]["journal"] == original_journal
finally:
os.unlink(temp_path)


# Deprecation warning tests for write_file and write_string
def test_write_file_deprecated_parse_stack_parameter():
"""Test that using deprecated 'parse_stack' parameter issues a warning."""
library = Library([])

with tempfile.NamedTemporaryFile(mode="w", suffix=".bib", delete=False) as f:
temp_path = f.name

try:
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
write_file(temp_path, library, parse_stack=[])
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "parse_stack" in str(w[0].message)
assert "unparse_stack" in str(w[0].message)
finally:
os.unlink(temp_path)


def test_write_file_deprecated_append_middleware_parameter():
"""Test that using deprecated 'append_middleware' parameter issues a warning."""
library = Library([])

with tempfile.NamedTemporaryFile(mode="w", suffix=".bib", delete=False) as f:
temp_path = f.name

try:
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
write_file(temp_path, library, append_middleware=[])
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "append_middleware" in str(w[0].message)
assert "prepend_middleware" in str(w[0].message)
finally:
os.unlink(temp_path)


def test_write_file_both_parse_stack_and_unparse_stack_raises_error():
"""Test that providing both parse_stack and unparse_stack raises ValueError."""
library = Library([])

with tempfile.NamedTemporaryFile(mode="w", suffix=".bib", delete=False) as f:
temp_path = f.name

try:
with pytest.raises(ValueError) as excinfo:
write_file(temp_path, library, parse_stack=[], unparse_stack=[])
assert "parse_stack" in str(excinfo.value)
assert "unparse_stack" in str(excinfo.value)
assert "Use 'unparse_stack' instead" in str(excinfo.value)
finally:
os.unlink(temp_path)


def test_write_file_both_append_and_prepend_middleware_raises_error():
"""Test that providing both append_middleware and prepend_middleware raises ValueError."""
library = Library([])

with tempfile.NamedTemporaryFile(mode="w", suffix=".bib", delete=False) as f:
temp_path = f.name

try:
with pytest.raises(ValueError) as excinfo:
write_file(temp_path, library, append_middleware=[], prepend_middleware=[])
assert "append_middleware" in str(excinfo.value)
assert "prepend_middleware" in str(excinfo.value)
assert "Use 'prepend_middleware' instead" in str(excinfo.value)
finally:
os.unlink(temp_path)


def test_write_file_unexpected_keyword_argument_raises_error():
"""Test that unexpected keyword arguments raise TypeError."""
library = Library([])

with tempfile.NamedTemporaryFile(mode="w", suffix=".bib", delete=False) as f:
temp_path = f.name

try:
with pytest.raises(TypeError) as excinfo:
write_file(temp_path, library, unknown_param="value")
assert "unexpected keyword arguments" in str(excinfo.value)
assert "unknown_param" in str(excinfo.value)
finally:
os.unlink(temp_path)


def test_write_string_deprecated_parse_stack_parameter():
"""Test that using deprecated 'parse_stack' parameter issues a warning."""
library = Library([])

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
write_string(library, parse_stack=[])
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "parse_stack" in str(w[0].message)
assert "unparse_stack" in str(w[0].message)


def test_write_string_deprecated_append_middleware_parameter():
"""Test that using deprecated 'append_middleware' parameter issues a warning."""
library = Library([])

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
write_string(library, append_middleware=[])
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "append_middleware" in str(w[0].message)
assert "prepend_middleware" in str(w[0].message)


def test_write_string_both_parse_stack_and_unparse_stack_raises_error():
"""Test that providing both parse_stack and unparse_stack raises ValueError."""
library = Library([])

with pytest.raises(ValueError) as excinfo:
write_string(library, parse_stack=[], unparse_stack=[])
assert "parse_stack" in str(excinfo.value)
assert "unparse_stack" in str(excinfo.value)
assert "Use 'unparse_stack' instead" in str(excinfo.value)


def test_write_string_both_append_and_prepend_middleware_raises_error():
"""Test that providing both append_middleware and prepend_middleware raises ValueError."""
library = Library([])

with pytest.raises(ValueError) as excinfo:
write_string(library, append_middleware=[], prepend_middleware=[])
assert "append_middleware" in str(excinfo.value)
assert "prepend_middleware" in str(excinfo.value)
assert "Use 'prepend_middleware' instead" in str(excinfo.value)


def test_write_string_unexpected_keyword_argument_raises_error():
"""Test that unexpected keyword arguments raise TypeError."""
library = Library([])

with pytest.raises(TypeError) as excinfo:
write_string(library, unknown_param="value")
assert "unexpected keyword arguments" in str(excinfo.value)
assert "unknown_param" in str(excinfo.value)
Loading