17
0
Files
python-bibtexparser/support-new-pyparsing.patch

341 lines
15 KiB
Diff
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
From f7d86562b0885ae1725a199fdcbc61fe5ee78a68 Mon Sep 17 00:00:00 2001
From: Jennifer Richards <jennifer@staff.ietf.org>
Date: Sat, 17 Jan 2026 15:10:16 -0400
Subject: [PATCH 1/3] Use modern names for pyparsing methods
---
bibtexparser/bibtexexpression.py | 50 ++++++++++-----------
bibtexparser/bparser.py | 12 ++---
bibtexparser/tests/test_bibtexexpression.py | 42 ++++++++---------
3 files changed, 52 insertions(+), 52 deletions(-)
diff --git a/bibtexparser/bibtexexpression.py b/bibtexparser/bibtexexpression.py
index 855d2e8f..ceace6df 100644
--- a/bibtexparser/bibtexexpression.py
+++ b/bibtexparser/bibtexexpression.py
@@ -29,7 +29,7 @@ def add_logger_parse_action(expr, log_func):
"""Register a callback on expression parsing with the adequate message."""
def action(s, l, t):
log_func("Found {}: {}".format(expr.resultsName, t))
- expr.addParseAction(action)
+ expr.add_parse_action(action)
# Parse action helpers
@@ -111,7 +111,7 @@ def __init__(self):
# String names
string_name = pp.Word(pp.alphanums + '_-:')('StringName')
self.set_string_name_parse_action(lambda s, l, t: None)
- string_name.addParseAction(self._string_name_parse_action)
+ string_name.add_parse_action(self._string_name_parse_action)
# Values inside bibtex fields
# Values can be integer or string expressions. The latter may use
@@ -123,27 +123,27 @@ def __init__(self):
# Braced values: braced values can contain nested (but balanced) braces
braced_value_content = pp.CharsNotIn('{}')
braced_value = pp.Forward() # Recursive definition for nested braces
- braced_value <<= pp.originalTextFor(
+ braced_value <<= pp.original_text_for(
'{' + pp.ZeroOrMore(braced_value | braced_value_content) + '}'
)('BracedValue')
- braced_value.setParseAction(remove_braces)
+ braced_value.set_parse_action(remove_braces)
# TODO add ignore for "\}" and "\{" ?
# TODO @ are not parsed by bibtex in braces
# Quoted values: may contain braced content with balanced braces
- brace_in_quoted = pp.nestedExpr('{', '}', ignoreExpr=None)
+ brace_in_quoted = pp.nested_expr('{', '}', ignore_expr=None)
text_in_quoted = pp.CharsNotIn('"{}')
# (quotes should be escaped by braces in quoted value)
- quoted_value = pp.originalTextFor(
+ quoted_value = pp.original_text_for(
'"' + pp.ZeroOrMore(text_in_quoted | brace_in_quoted) + '"'
)('QuotedValue')
- quoted_value.addParseAction(pp.removeQuotes)
+ quoted_value.add_parse_action(pp.remove_quotes)
# String expressions
- string_expr = pp.delimitedList(
+ string_expr = pp.DelimitedList(
(quoted_value | braced_value | string_name), delim='#'
)('StringExpression')
- string_expr.addParseAction(self._string_expr_parse_action)
+ string_expr.add_parse_action(self._string_expr_parse_action)
value = (integer | string_expr)('Value')
@@ -151,7 +151,7 @@ def __init__(self):
# @EntryType { ...
entry_type = (pp.Suppress('@') + pp.Word(pp.alphas))('EntryType')
- entry_type.setParseAction(first_token)
+ entry_type.set_parse_action(first_token)
# Entry key: any character up to a ',' without leading and trailing
# spaces. Also exclude spaces and prevent it from being empty.
@@ -175,20 +175,20 @@ def citekeyParseAction(string_, location, token):
msg="Whitespace not allowed in citekeys.")
return key
- key.setParseAction(citekeyParseAction)
+ key.set_parse_action(citekeyParseAction)
# Field name: word of letters, digits, dashes and underscores
field_name = pp.Word(pp.alphanums + '_-().+')('FieldName')
- field_name.setParseAction(first_token)
+ field_name.set_parse_action(first_token)
# Field: field_name = value
field = pp.Group(field_name + pp.Suppress('=') + value)('Field')
- field.setParseAction(field_to_pair)
+ field.set_parse_action(field_to_pair)
# List of fields: comma separeted fields
- field_list = (pp.delimitedList(field) + pp.Suppress(pp.Optional(','))
+ field_list = (pp.DelimitedList(field) + pp.Suppress(pp.Optional(','))
)('Fields')
- field_list.setParseAction(
+ field_list.set_parse_action(
lambda s, l, t: {k: v for (k, v) in reversed(t.get('Fields'))})
# Entry: type, key, and fields
@@ -204,10 +204,10 @@ def citekeyParseAction(string_, location, token):
) | pp.StringEnd()
self.explicit_comment = (
pp.Suppress(comment_line_start) +
- pp.originalTextFor(pp.SkipTo(not_an_implicit_comment),
- asString=True))('ExplicitComment')
- self.explicit_comment.addParseAction(remove_trailing_newlines)
- self.explicit_comment.addParseAction(remove_braces)
+ pp.original_text_for(pp.SkipTo(not_an_implicit_comment),
+ as_string=True))('ExplicitComment')
+ self.explicit_comment.add_parse_action(remove_trailing_newlines)
+ self.explicit_comment.add_parse_action(remove_braces)
# Previous implementation included comment until next '}'.
# This is however not inline with bibtex behavior that is to only
# ignore until EOL. Brace stipping is arbitrary here but avoids
@@ -219,10 +219,10 @@ def mustNotBeEmpty(t):
raise pp.ParseException("Match must not be empty.")
# Implicit comments: not anything else
- self.implicit_comment = pp.originalTextFor(
- pp.SkipTo(not_an_implicit_comment).setParseAction(mustNotBeEmpty),
- asString=True)('ImplicitComment')
- self.implicit_comment.addParseAction(remove_trailing_newlines)
+ self.implicit_comment = pp.original_text_for(
+ pp.SkipTo(not_an_implicit_comment).set_parse_action(mustNotBeEmpty),
+ as_string=True)('ImplicitComment')
+ self.implicit_comment.add_parse_action(remove_trailing_newlines)
# String definition
self.string_def = (pp.Suppress(string_def_start) + in_braces_or_pars(
@@ -274,5 +274,5 @@ def _string_name_parse_action(self, s, l, t):
def _string_expr_parse_action(self, s, l, t):
return BibDataStringExpression.expression_if_needed(t)
- def parseFile(self, file_obj):
- return self.main_expression.parseFile(file_obj, parseAll=True)
+ def parse_file(self, file_obj):
+ return self.main_expression.parse_file(file_obj, parse_all=True)
diff --git a/bibtexparser/bparser.py b/bibtexparser/bparser.py
index cbb1dbae..811f25c8 100644
--- a/bibtexparser/bparser.py
+++ b/bibtexparser/bparser.py
@@ -159,7 +159,7 @@ def parse(self, bibtex_str, partial=False):
bibtex_file_obj = self._bibtex_file_obj(bibtex_str)
try:
- self._expr.parseFile(bibtex_file_obj)
+ self._expr.parse_file(bibtex_file_obj)
except self._expr.ParseException as exc:
logger.error("Could not parse properly, starting at %s", exc.line)
if not partial:
@@ -198,20 +198,20 @@ def _init_expressions(self):
self._expr.add_log_function(logger.debug)
# Set actions
- self._expr.entry.addParseAction(
+ self._expr.entry.add_parse_action(
lambda s, l, t: self._add_entry(
t.get('EntryType'), t.get('Key'), t.get('Fields'))
)
- self._expr.implicit_comment.addParseAction(
+ self._expr.implicit_comment.add_parse_action(
lambda s, l, t: self._add_comment(t[0])
)
- self._expr.explicit_comment.addParseAction(
+ self._expr.explicit_comment.add_parse_action(
lambda s, l, t: self._add_comment(t[0])
)
- self._expr.preamble_decl.addParseAction(
+ self._expr.preamble_decl.add_parse_action(
lambda s, l, t: self._add_preamble(t[0])
)
- self._expr.string_def.addParseAction(
+ self._expr.string_def.add_parse_action(
lambda s, l, t: self._add_string(t['StringName'].name,
t['StringValue'])
)
diff --git a/bibtexparser/tests/test_bibtexexpression.py b/bibtexparser/tests/test_bibtexexpression.py
index 1bcab434..e2e878fd 100644
--- a/bibtexparser/tests/test_bibtexexpression.py
+++ b/bibtexparser/tests/test_bibtexexpression.py
@@ -14,90 +14,90 @@ def setUp(self):
self.expr = BibtexExpression()
def test_minimal(self):
- result = self.expr.entry.parseString('@journal{key, name = 123 }')
+ result = self.expr.entry.parse_string('@journal{key, name = 123 }')
self.assertEqual(result.get('EntryType'), 'journal')
self.assertEqual(result.get('Key'), 'key')
self.assertEqual(result.get('Fields'), {'name': '123'})
def test_capital_type(self):
- result = self.expr.entry.parseString('@JOURNAL{key, name = 123 }')
+ result = self.expr.entry.parse_string('@JOURNAL{key, name = 123 }')
self.assertEqual(result.get('EntryType'), 'JOURNAL')
def test_capital_key(self):
- result = self.expr.entry.parseString('@journal{KEY, name = 123 }')
+ result = self.expr.entry.parse_string('@journal{KEY, name = 123 }')
self.assertEqual(result.get('Key'), 'KEY')
def test_braced(self):
- result = self.expr.entry.parseString('@journal{key, name = {abc} }')
+ result = self.expr.entry.parse_string('@journal{key, name = {abc} }')
self.assertEqual(result.get('Fields'), {'name': 'abc'})
def test_braced_with_new_line(self):
- result = self.expr.entry.parseString(
+ result = self.expr.entry.parse_string(
'@journal{key, name = {abc\ndef} }')
self.assertEqual(result.get('Fields'), {'name': 'abc\ndef'})
def test_braced_unicode(self):
- result = self.expr.entry.parseString(
+ result = self.expr.entry.parse_string(
'@journal{key, name = {àbcđéf} }')
self.assertEqual(result.get('Fields'), {'name': 'àbcđéf'})
def test_quoted(self):
- result = self.expr.entry.parseString('@journal{key, name = "abc" }')
+ result = self.expr.entry.parse_string('@journal{key, name = "abc" }')
self.assertEqual(result.get('Fields'), {'name': 'abc'})
def test_quoted_with_new_line(self):
- result = self.expr.entry.parseString(
+ result = self.expr.entry.parse_string(
'@journal{key, name = "abc\ndef" }')
self.assertEqual(result.get('Fields'), {'name': 'abc\ndef'})
def test_quoted_with_unicode(self):
- result = self.expr.entry.parseString(
+ result = self.expr.entry.parse_string(
'@journal{key, name = "àbcđéf" }')
self.assertEqual(result.get('Fields'), {'name': 'àbcđéf'})
def test_entry_declaration_after_space(self):
- self.expr.entry.parseString(' @journal{key, name = {abcd}}')
+ self.expr.entry.parse_string(' @journal{key, name = {abcd}}')
def test_entry_declaration_no_key(self):
with self.assertRaises(self.expr.ParseException):
- self.expr.entry.parseString('@misc{name = {abcd}}')
+ self.expr.entry.parse_string('@misc{name = {abcd}}')
def test_entry_declaration_no_key_new_line(self):
with self.assertRaises(self.expr.ParseException):
- self.expr.entry.parseString('@misc{\n name = {abcd}}')
+ self.expr.entry.parse_string('@misc{\n name = {abcd}}')
def test_entry_declaration_no_key_comma(self):
with self.assertRaises(self.expr.ParseException):
- self.expr.entry.parseString('@misc{, \nname = {abcd}}')
+ self.expr.entry.parse_string('@misc{, \nname = {abcd}}')
def test_entry_declaration_no_key_keyvalue_without_space(self):
with self.assertRaises(self.expr.ParseException):
- self.expr.entry.parseString('@misc{\nname=aaa}')
+ self.expr.entry.parse_string('@misc{\nname=aaa}')
def test_entry_declaration_key_with_whitespace(self):
with self.assertRaises(self.expr.ParseException):
- self.expr.entry.parseString('@misc{ xxyy, \n name = aaa}')
+ self.expr.entry.parse_string('@misc{ xxyy, \n name = aaa}')
def test_string_declaration_after_space(self):
- self.expr.string_def.parseString(' @string{ name = {abcd}}')
+ self.expr.string_def.parse_string(' @string{ name = {abcd}}')
def test_preamble_declaration_after_space(self):
- self.expr.preamble_decl.parseString(' @preamble{ "blah blah " }')
+ self.expr.preamble_decl.parse_string(' @preamble{ "blah blah " }')
def test_declaration_after_space(self):
keys = []
- self.expr.entry.addParseAction(
+ self.expr.entry.add_parse_action(
lambda s, l, t: keys.append(t.get('Key'))
)
- self.expr.main_expression.parseString(' @journal{key, name = {abcd}}')
+ self.expr.main_expression.parse_string(' @journal{key, name = {abcd}}')
self.assertEqual(keys, ['key'])
def test_declaration_after_space_and_comment(self):
keys = []
- self.expr.entry.addParseAction(
+ self.expr.entry.add_parse_action(
lambda s, l, t: keys.append(t.get('Key'))
)
- self.expr.main_expression.parseString(
+ self.expr.main_expression.parse_string(
'% Implicit comment\n @article{key, name={abcd}}'
)
self.assertEqual(keys, ['key'])
From ed312f7825296b20339319913e9dc3b33df8cab1 Mon Sep 17 00:00:00 2001
From: Jennifer Richards <jennifer@staff.ietf.org>
Date: Sat, 17 Jan 2026 15:11:11 -0400
Subject: [PATCH 2/3] Update pyparsing requirement
---
requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index e7c21d1a..f83eb78c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1 @@
-pyparsing>=2.0.3
+pyparsing>=3.0.0
From 808cb4d54517e8378234f2cd56853c018ecb086c Mon Sep 17 00:00:00 2001
From: Jennifer Richards <jennifer@staff.ietf.org>
Date: Sat, 17 Jan 2026 19:04:57 -0400
Subject: [PATCH 3/3] Alias method for backward compatibility
---
bibtexparser/bibtexexpression.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/bibtexparser/bibtexexpression.py b/bibtexparser/bibtexexpression.py
index ceace6df..779a40aa 100644
--- a/bibtexparser/bibtexexpression.py
+++ b/bibtexparser/bibtexexpression.py
@@ -275,4 +275,12 @@ def _string_expr_parse_action(self, s, l, t):
return BibDataStringExpression.expression_if_needed(t)
def parse_file(self, file_obj):
+ """Execute parse expression on a file object"""
return self.main_expression.parse_file(file_obj, parse_all=True)
+
+ def parseFile(self, file_obj):
+ """Execute parse expression on a file object
+
+ Alias for parse_file()
+ """
+ return self.parse_file(file_obj)