Skip to content

test(md): keep every line of pipe-led text that has no delimiter row - #4454

Closed
breken-ai wants to merge 1 commit into
docling-project:mainfrom
breken-ai:fix/md-table-without-delimiter-row
Closed

breken-ai wants to merge 1 commit into
docling-project:mainfrom
breken-ai:fix/md-table-without-delimiter-row

Conversation

@breken-ai

@breken-ai breken-ai commented Sep 30, 2026 •

Copy link
Copy Markdown

Update (2026-10-07): #4488 moved Markdown table parsing to marko's GFM extension, and that fixes the bug below on main. This PR now adds only the regression test. The original backend change has been dropped.

The bug (fixed on main by #4488): the Markdown backend dropped a line of text when a paragraph started with a pipe but had no GFM delimiter row. _close_table always threw away the second buffered row, assuming it was the |---|---| delimiter row. This shows up with Pandoc line blocks (addresses, verse) and with pipe tables written without a delimiter row:

Ship to:

| Jane Doe
| 221B Baker Street
| London NW1 6XE

Before #4488, export_to_markdown() gave a two-row table and the street line was gone. | a | b | / | 1 | 2 | / | 3 | 4 | lost 1 | 2 in the same way.

Change: test_convert_leading_pipe_lines_without_delimiter_row_keep_all_text checks that pipe-led lines with no delimiter row stay one text item with every line kept, and that no table is built. It covers a delimiter-less pipe table and a pipe-led line followed by plain text.

Tests:

I found this with help from an AI coding assistant, and I reviewed the test and its results myself.

Checklist:

  • Documentation has been updated, if necessary.
  • Examples have been added, if necessary.
  • Tests have been added, if necessary.

@github-actions

Copy link
Copy Markdown
Contributor

✅ DCO Check Passed

Thanks @breken-ai, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ceberam ceberam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @breken-ai for spotting this issue. The bug is real and the diagnosis is accurate.
The fix is correct, well-scoped, and does not regress existing behavior.
Please, address the inline comment and the PR will be ready to be merged.

Comment thread docling/backend/md_backend.py Outdated
Comment on lines +350 to +352
# GFM: a table needs a delimiter row right after its header, so
# lines that merely start with a pipe are a paragraph. Building a
# table here would drop the second line, taken for that row.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first sentence is useful: it states the GFM rule. The second sentence explains what the old, buggy code did, not what a future maintainer needs to know. It should be removed or rewritten to describe the invariant. For example:

Suggested change
# GFM: a table needs a delimiter row right after its header, so
# lines that merely start with a pipe are a paragraph. Building a
# table here would drop the second line, taken for that row.
# GFM: a table requires a delimiter row immediately after its header.
# Without one, lines that start with a pipe are ordinary paragraph text.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied your wording in the test docstring (4361aea). The _close_table change itself is gone, because #4488 replaced that parsing path. Details in the PR conversation.

@ceberam

ceberam commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

@breken-ai Please check the PR #4488
I have refactored the table parsing to leverage marko GFM extension. Instead of a manual string manipulation to parse tables (and the challenges to deal with complex edge cases like this one), we can leverage the Markdown(extensions=["gfm"]):

  • marko natively understands the GFM Table specification.
  • The parser itself produces a clean, structured tree of table nodes.

I checked and with that approach the case in this PR is no longer an issue, since marko does not recognize Pandoc line blocks as tables.

GFM requires a delimiter row right after a table header. Without one,
lines that start with a pipe are paragraph text. docling-project#4488 moved table parsing
to marko's GFM extension, which fixes this; this test guards it. It fails
on 1a264db~1 (the text is parsed as a table) and passes on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
@breken-ai
breken-ai force-pushed the fix/md-table-without-delimiter-row branch from 1232b35 to 4361aea Compare October 8, 2026 00:17
@breken-ai breken-ai changed the title fix(md): keep every line of pipe-led text that has no delimiter row test(md): keep every line of pipe-led text that has no delimiter row Oct 8, 2026
@breken-ai

Copy link
Copy Markdown
Author

@ceberam thanks, I checked #4488 and you're right. With the marko GFM parser, pipe-led lines that have no delimiter row are no longer read as a table.

So I've dropped the _close_table change, since that code path no longer exists after #4488 and the branch conflicted with it. The PR is now test-only (4361aea). It keeps test_convert_leading_pipe_lines_without_delimiter_row_keep_all_text as a regression guard for the new parser. The docstring uses your wording and describes the invariant.

  • It fails on 1a264db~1 with assert [TableItem(...)] == [] and passes on main.
  • tests/test_backend_markdown.py passes, 34 of 34. ruff check and ruff format --check are clean.

If you'd rather not take the extra test, feel free to close this.

@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) markdown issue related to markdown backend tests issue related to changes needed in the tests labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

AI triage (advisory)

Checked commit 4361aeaa46. The analysis read the diff only. It did not run code from this pull request.

This PR adds a regression test that checks pipe-led lines without a GFM delimiter row are kept as plain text with no lines dropped and no table created. The underlying bug was already fixed by PR #4488.

Topic labels added: markdown, tests.

CI safety: no concern found. A maintainer must still approve CI.

1 related pull request(s) in the same code
PR State Signal Assessment
#4488 merged referenced as #4488 related: PR #4488 (merged) fixed the bug and added extensive inline-formatting tests for the Markdown table backend. This PR adds only a focused regression test for the pipe-led-lines-without-delimiter-row case that #4488 also fixed; they touch the same file but do different things and neither blocks the other from being merged.

The ai:* labels are advisory. They do not approve CI and they do not add tests:full. A new push runs this check again.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI first review (advisory): no blocking issue found

This review covers the full diff at 4361aeaa46. It read the code only. It did not run tests or code from this pull request.

This PR adds a single regression test test_convert_leading_pipe_lines_without_delimiter_row_keep_all_text to tests/test_backend_markdown.py. It guards against a bug (fixed on main by PR #4488) where _close_table dropped one buffered row when pipe-led lines had no GFM delimiter row. No production code is changed. The main risk is that the two hardcoded expected strings depend on marko's exact soft-line-break join behavior; a change in marko's internal representation could cause spurious failures.

A maintainer makes the merge decision. This review never approves the pull request.

and every line of it must be kept.
"""
cases = [
("| a | b |\n| 1 | 2 |\n| 3 | 4 |\n", "| a | b | | 1 | 2 | | 3 | 4 |"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: Expected strings are brittle against marko's join representation

The expected value "| a | b | | 1 | 2 | | 3 | 4 |" is derived from the backend joining lines with a single space (soft-break path at md_backend.py:898). This is currently correct, but any change in marko's tokenisation of pipe-surrounded text — for example if marko ever strips leading/trailing pipes from non-table paragraph lines — would silently produce a wrong expected value rather than a meaningful failure message.

Consider adding a brief comment to the tuple explaining the join rule:

Suggested change
("| a | b |\n| 1 | 2 |\n| 3 | 4 |\n", "| a | b | | 1 | 2 | | 3 | 4 |"),
# Lines are joined by the soft-break path (md_backend.py) with a single space.
("| a | b |\n| 1 | 2 |\n| 3 | 4 |\n", "| a | b | | 1 | 2 | | 3 | 4 |"),

conv_result = get_converter().convert_string(markdown, format=InputFormat.MD)
assert conv_result.status == ConversionStatus.SUCCESS
assert conv_result.document.tables == []
assert [item.text for item in conv_result.document.texts] == [expected]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: document.texts includes CodeItem; unfiltered list comparison may mask future regressions

DoclingDocument.texts is a flat list that includes CodeItem instances alongside TextItem instances (see test line 270 which filters isinstance(item, CodeItem)). For the current inputs this is harmless — no code spans are present — but if the test inputs were ever extended with backtick content, the unfiltered assertion would produce a confusing failure. Consider adding an isinstance guard or a comment explaining that the assertion intentionally covers all text-like items for these inputs.

assert conv_result.document.tables == []


def test_convert_leading_pipe_lines_without_delimiter_row_keep_all_text():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Pandoc line-block format from the PR description is not covered

The PR body uses a Pandoc address example (| Jane Doe\n| 221B Baker Street\n| London NW1 6XE\n) as the primary motivating case, but the test uses abstract | a | b | rows. Adding the address-format case (or a comment linking the motivation) would make the regression guard easier to understand when it fails in the future.

@github-actions github-actions Bot added the ai:review-lgtm AI first review found no blocker or major issue (advisory) label Oct 8, 2026
@ceberam

ceberam commented Oct 23, 2026

Copy link
Copy Markdown
Member

Closing as agreed on #4454 (comment)

@ceberam ceberam closed this Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai:ci-safe AI triage found no CI-safety concern (advisory) ai:review-lgtm AI first review found no blocker or major issue (advisory) markdown issue related to markdown backend tests issue related to changes needed in the tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants