Conversation
A GFM row is split on every unescaped pipe, and the cell's text reached the
output unchanged. A regex alternation, a shell command or a part number in a
cell therefore pushed the rest of the row into columns the header does not
have:
| Name | Modes |
| --- | --- |
| codec | a|b|c |
Read back, that row has four cells against a two-column header, and every row
after it is out of step.
Escape the pipes in a cell. The run of backslashes in front of the pipe is
matched rather than the pipe alone, because escape_md_section leaves a
backslash before a pipe untouched (`|` is not in RE_SLASH_CHARS), so a cell
holding `a\|b` would otherwise have its own backslash consume the escape.
CustomHTML2Text returns early for inline code and pre without going through
the base escaping, so both of those call it too: a code span does not protect
a pipe, GFM splits the row first.
`pad_tables` reformats the finished table by splitting each row on every "|", so the pipe a cell escapes was turned straight back into a column boundary and the padded table came out wider than its header.
|
ok |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A GFM table row is split on every unescaped pipe, and
html2textwrites a cell's text through unchanged. A cell that holds a pipe therefore adds a column.Measured on
main(862f6bc):Read back, that last row has four cells against a two-column header. Everything after it is one column out of step, and a reader assigns
bandcto columns that do not exist.It is not an exotic cell value for a crawler. A pipe turns up in regex alternations, shell commands (
grep -E 'a|b'), part numbers, "either/or" notes, and anything pasted out of a terminal — which is to say, in exactly the documentation and reference pages people crawl.The fix
escape_table_cell_pipesescapes the pipes while the converter is inside a<td>/<th>, tracked with a single flag set in the existing table branch ofhandle_tag.Two details worth calling out:
escape_md_sectionleaves a backslash before a pipe untouched, because|is not inRE_SLASH_CHARS. A cell holdinga\|bwould otherwise come out asa\\|b, where the cell's own backslash escapes the one we added and the pipe splits the row anyway. Doubling the run first givesa\\\|b, which reads back as the literala\|b.CustomHTML2Textcalls it too. Itshandle_datareturns early forinside_preandinside_codeand writes straight toself.o, so a pipe inside a code span in a cell never reached the base escaping. A code span does not protect a pipe — GFM splits the row before it looks at backticks — so both early returns escape as well.ignore_tablesandbypass_tablesreturn from the branch above, so the flag never gets set for them and their output is byte-identical. A pipe outside a table is untouched.The table padder had to agree.
pad_tablesreformats the finished table by splitting each row on every"|", which turned the escaped pipe straight back into a column boundary:reformat_tablenow splits on the unescaped pipes, through onesplit_table_rowhelper used at all three of its split sites.PAD_TABLESdefaults toFalse, so this only shows up for callers that turn it on — but leaving it would have traded one broken table for another.Tests
tests/unit/test_html2text_table_cell_pipe.py, 18 cases — every one parameterised over bothHTML2TextandCustomHTML2Text. The helper reads the produced table back the way a reader does (split on unescaped pipes, then resolve the CommonMark escapes) and asserts on the cells, not on a fixed string, so the test pins the property rather than the formatting.Covered:
a|b|c, a part number, a cell with its owna\|b, a header cell, a pipe inside inline code, the same table underpad_tables=True, plus guards forC:\path, a plain cell, a pipe in a paragraph outside any table,bypass_tables, and a plain table underpad_tables=True.Measured:
main, tests kepttests/unit+ the markdown suites:111 passedwith the change against89 passedwithout it, and the same28 failedon both sides — those are pre-existing failures intest_sitemap_namespace_parsing.pyand friends, unrelated to this change.