Repository: freelawproject/eyecite

Issue:
`maybe_balance_style_tags` needs regex escaping
Noticed while writing tests for https://github.com/freelawproject/courtlistener/pull/5114/files
```python
import re

string =  "Something; In <em>Nobelman </em> at 332, 113 S.Ct. 2106 (2010); Something else"
span_text = "Nobelman </em> at 332, 113 S.Ct. 2106 (2010)"
opening_tag = "<em>"
regex = rf"{opening_tag}\s*{span_text}"

re.search(regex, string)
# nothing....


escaped_regex = rf"{re.escape(opening_tag)}\s*{re.escape(span_text)}"
re.search(escaped_regex, string)
Out[3]: <re.Match object; span=(14, 62), match='<em>Nobelman </em> at 332, 113 S.Ct. 2106 (2010)'>
```

Hints from issue comments:

Solution patch:
diff --git a/CHANGES.md b/CHANGES.md
index c26729f..fe69f23 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,17 +16,16 @@ Fixes:
 
 ## Current
 
+**2.6.11 - 2025-02-20**
+
+Fixes:
+- Add regex escapig to `utils.maybe_balance_style_tags`
+
 **2.6.10 - 2025-02-20**
 
 Features:
 - Adds support to correct citation page
 
-Changes:
-- None
-
-Fixes:
-- None
-
 ## Past
 
 **2.6.9 - 2025-02-20**
diff --git a/eyecite/utils.py b/eyecite/utils.py
index 5c21d4b..afb46de 100644
--- a/eyecite/utils.py
+++ b/eyecite/utils.py
@@ -288,7 +288,7 @@ def maybe_balance_style_tags(
                 end + len(closing_tag) + tolerance, len(plain_text)
             )
             if end_match := re.search(
-                rf"{span_text}\s*{closing_tag}",
+                rf"{re.escape(span_text)}\s*{re.escape(closing_tag)}",
                 plain_text[start:extended_end],
                 flags=re.MULTILINE,
             ):
@@ -298,7 +298,7 @@ def maybe_balance_style_tags(
             # look for opening tag before the start
             extended_start = min(start - len(opening_tag) - tolerance, 0)
             if start_match := re.search(
-                rf"{opening_tag}\s*{span_text}",
+                rf"{re.escape(opening_tag)}\s*{re.escape(span_text)}",
                 plain_text[extended_start:end],
                 flags=re.MULTILINE,
             ):
diff --git a/pyproject.toml b/pyproject.toml
index e9c980e..764fa93 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.poetry]
-version = "2.6.10"
+version = "2.6.11"
 authors = ["Free Law Project <info@free.law>"]
 classifiers = [
   "Development Status :: 5 - Production/Stable",


Test patch:
diff --git a/tests/test_AnnotateTest.py b/tests/test_AnnotateTest.py
index 939abe9..46a49b2 100644
--- a/tests/test_AnnotateTest.py
+++ b/tests/test_AnnotateTest.py
@@ -1,7 +1,9 @@
+import re
 from pathlib import Path
 from unittest import TestCase
 
 from eyecite import annotate_citations, clean_text, get_citations
+from eyecite.utils import maybe_balance_style_tags
 
 
 class AnnotateTest(TestCase):
@@ -230,6 +232,14 @@ class AnnotateTest(TestCase):
                 )
                 self.assertEqual(annotated, expected)
 
+    def test_tag_balancing(self):
+        """Test trickier tag balancing cases"""
+        string = "Something; In <em>Nobelman </em> at 332, 113 S.Ct. 2106 (2010); Something else"
+        span_text = "Nobelman </em> at 332, 113 S.Ct. 2106 (2010)"
+        start, end = re.search(re.escape(span_text), string).span()
+        _, _, balanced = maybe_balance_style_tags(start, end, string)
+        self.assertTrue(balanced.startswith("<em>"))
+
     def test_long_diff(self):
         """Does diffing work across a long text with many changes?"""
         opinion_text = (