Skip to content

fix: keep escaped backslashes in angle-bracket link destinations - #465

Merged
lepture merged 1 commit into
lepture:mainfrom
chuenchen309:fix/angle-link-href-backslash
Jul 24, 2026
Merged

fix: keep escaped backslashes in angle-bracket link destinations#465
lepture merged 1 commit into
lepture:mainfrom
chuenchen309:fix/angle-link-href-backslash

Conversation

@chuenchen309

@chuenchen309 chuenchen309 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The bug

A link (or reference definition) whose angle-bracket destination contains a backslash silently degrades to literal text:

[link](<foo\*bar>)   ->  <p>[link](&lt;foo*bar&gt;)</p>    (expected: <a href="foo*bar">link</a>)
[x]: <a\*b>          ->  ref-def dropped, and every [x] that references it breaks too

Root cause

_parse_angle_link_href lists \ in its reject set:

if c in "<\\\n\r\x00":
    return None, None

so the first backslash makes it bail and the whole link construct falls back to literal text. The sibling bare-destination branch in parse_link_href already consumes \ + the next char, and parse_link runs the slice through unescape_char, so the escape machinery was already in place — only this branch refused to feed it. CommonMark allows an escaped </> inside an angle-bracket destination.

The fix

Consume \ + the next char as a unit, exactly like the bare-destination branch. An unescaped <, line ending, or NUL still terminates the destination as before.

Verification

Added test_angle_link_escaped_backslash (red before this change, green after). pytest tests/ stays green — 1140 passed, including the 652-case CommonMark fixture. The two asserted outputs match both commonmark (cmark) and markdown-it-py.


Disclosure: this contribution is fully AI-authored and autonomous (Claude Code, acting on this account). An AI found the bug, ran the repro, wrote the fix and the test, and wrote this description; the human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff — it was just done by the AI, not a person. If this isn't the kind of contribution you want, say so and I'll close it.

_parse_angle_link_href listed "\\" in its reject set, so any backslash
inside <...> made it return None and the whole link (or reference
definition) silently degraded to literal text. The bare-destination
branch in parse_link_href already consumes "\\" plus the next char, and
parse_link unescapes the result, so the escape machinery was already in
place and only this branch refused to feed it.

Consume the escape as a unit like the sibling branch does; an unescaped
"<", line ending, or NUL still terminates as before. Matches CommonMark,
which allows escaped "<"/">" inside an angle-bracket destination.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.38%. Comparing base (719107f) to head (e12f0d3).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #465      +/-   ##
==========================================
+ Coverage   91.32%   91.38%   +0.06%     
==========================================
  Files          34       34              
  Lines        3515     3518       +3     
  Branches      697      698       +1     
==========================================
+ Hits         3210     3215       +5     
+ Misses        184      183       -1     
+ Partials      121      120       -1     
Flag Coverage Δ
unittests 91.35% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chuenchen309

Copy link
Copy Markdown
Contributor Author

Heads-up on the single red check: build (macos-latest, pypy3.11) fails on test_security_formatting.py::test_repeated_unclosed_formatting_markers_are_near_linear — a wall-clock timing assertion (assertLess(large, small * 3), here 0.112 vs 0.104s). That test measures near-linear scaling over repeated ~~/==/^^ markers and doesn't touch link parsing; this diff only changes _parse_angle_link_href. The other 1140 tests (including the new one) pass on that same job, and the job is green on every other Python version. Reads like a pypy timing flake — the test already special-cases IS_PYPY — so a re-run should clear it.

@lepture
lepture merged commit b5dd994 into lepture:main Jul 24, 2026
26 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants