Skip to content

Commit 6d6a41c

Browse files
Merge pull request #462 from Bala-Sakabattula/pypandoc-format-fix
pypandoc tex format fix
2 parents 48fa1da + bbd8d36 commit 6d6a41c

3 files changed

Lines changed: 72 additions & 3 deletions

File tree

sync2jira/downstream_issue.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,33 @@ def sync_with_jira(issue, config):
15211521
retry = True
15221522

15231523

1524+
def convert_content(content: str) -> str:
1525+
"""Convert GitHub Flavored Markdown to Jira format via pypandoc.
1526+
1527+
Falls back to disabling TeX math extensions if the initial conversion
1528+
fails (e.g. content misinterpreted as LaTeX). Returns the original
1529+
content unchanged if both attempts fail.
1530+
1531+
:param str content: GitHub Flavored Markdown text
1532+
:returns: Jira-formatted text, or the original content on failure
1533+
:rtype: str
1534+
"""
1535+
try:
1536+
content = pypandoc.convert_text(content, "jira", format="gfm")
1537+
except Exception as e:
1538+
log.warning("pypandoc conversion failed, retrying with TeX disabled: %s", e)
1539+
try:
1540+
sanitized = content.replace("\\", "\\\\").replace("$", "\\$")
1541+
content = pypandoc.convert_text(
1542+
sanitized,
1543+
"jira",
1544+
format="gfm-tex_math_dollars-tex_math_single_backslash",
1545+
)
1546+
except Exception as e2:
1547+
log.warning("pypandoc fallback also failed; using raw content: %s", e2)
1548+
return content
1549+
1550+
15241551
def update_jira(client, config, issue):
15251552
# Check the status of the JIRA client
15261553
if not config["sync2jira"]["develop"] and not check_jira_status(client):
@@ -1533,7 +1560,7 @@ def update_jira(client, config, issue):
15331560
and issue.content
15341561
and "github_markdown" in issue.downstream["issue_updates"]
15351562
):
1536-
issue.content = pypandoc.convert_text(issue.content, "jira", format="gfm")
1563+
issue.content = convert_content(issue.content)
15371564

15381565
# First, check to see if we have a matching issue using the new method.
15391566
# If we do, then bail out. No sync needed.

sync2jira/downstream_pr.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from jira import JIRAError
2424
from jira.client import Issue as JIRAIssue
2525
from jira.client import ResultList
26-
import pypandoc
2726

2827
# Local Modules
2928
import sync2jira.downstream_issue as d_issue
@@ -266,7 +265,7 @@ def _create_jira_issue_from_pr(client, pr, config):
266265
and pr_content
267266
and "github_markdown" in pr.downstream["issue_updates"]
268267
):
269-
pr_content = pypandoc.convert_text(pr_content, "jira", format="gfm")
268+
pr_content = d_issue.convert_content(pr_content)
270269

271270
# Convert PR to Issue-like object for creation
272271
# PR and Issue share similar structure, but we need to adapt it

tests/test_downstream_issue.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,49 @@ def test_sync_with_jira_no_matching(
11101110
)
11111111
mock_existing_jira_issue_legacy.assert_not_called()
11121112

1113+
@mock.patch(PATH + "pypandoc")
1114+
def test_convert_content(self, mock_pypandoc):
1115+
"""convert_content(): normal success, TeX fallback, and double failure."""
1116+
# Case 1: Normal GFM conversion succeeds
1117+
mock_pypandoc.convert_text.return_value = "normal markdown"
1118+
1119+
result = d.convert_content("normal markdown")
1120+
1121+
mock_pypandoc.convert_text.assert_called_once_with(
1122+
"normal markdown", "jira", format="gfm"
1123+
)
1124+
self.assertEqual(result, "normal markdown")
1125+
1126+
# Case 2: First call fails (TeX error), fallback succeeds
1127+
mock_pypandoc.convert_text.reset_mock()
1128+
mock_pypandoc.convert_text.side_effect = [
1129+
RuntimeError("Error producing PDF"),
1130+
"fallback converted",
1131+
]
1132+
1133+
result = d.convert_content("bad $tex")
1134+
1135+
self.assertEqual(mock_pypandoc.convert_text.call_count, 2)
1136+
first_call = mock_pypandoc.convert_text.call_args_list[0]
1137+
self.assertEqual(first_call[1]["format"], "gfm")
1138+
1139+
fallback_call = mock_pypandoc.convert_text.call_args_list[1]
1140+
self.assertEqual(
1141+
fallback_call[1]["format"],
1142+
"gfm-tex_math_dollars-tex_math_single_backslash",
1143+
)
1144+
self.assertEqual(fallback_call[0][0], "bad \\$tex")
1145+
self.assertEqual(result, "fallback converted")
1146+
1147+
# Case 3: Both calls fail, raw content preserved
1148+
mock_pypandoc.convert_text.reset_mock()
1149+
mock_pypandoc.convert_text.side_effect = RuntimeError("pandoc broken")
1150+
1151+
result = d.convert_content("raw content")
1152+
1153+
self.assertEqual(mock_pypandoc.convert_text.call_count, 2)
1154+
self.assertEqual(result, "raw content")
1155+
11131156
@mock.patch(PATH + "_update_title")
11141157
@mock.patch(PATH + "_update_description")
11151158
@mock.patch(PATH + "_update_comments")

0 commit comments

Comments
 (0)