Skip to content

blocks with multiline style breaking in 2.5.5 #689

@kjaymiller

Description

@kjaymiller

After upgrading to the latest version of markdown2 (v2.5.5). I began to get an AssertionError.

AI Attestation: I troubleshot this using Claude Code Opus4.6. Claude Code helped to write this summary but I did verify the issue and the working fix

The error occurs in _hash_html_block_sub at line 927:

File "markdown2.py", line 927, in _hash_html_block_sub
    assert m is not None
           ^^^^^^^^^^^^^
AssertionError

The file in question has a <div> tag with a style attribute that wraps across lines:

<div style="position: relative; width: 100%; height: 0; padding-top: 56.2500%;
 padding-bottom: 0; box-shadow: 0 2px 8px 0 rgba(63,69,81,0.16); margin-top: 1.6em; margin-bottom: 0.9em; overflow: hidden;
 border-radius: 8px; will-change: transform;">
  <iframe loading="lazy" title="2024 Year in Review Presentation" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: none; padding: 0;margin: 0;"
    src="https://www.canva.com/design/DAGbnweUoGY/-WaS6yuP3nwUW_lEH4TNKA/view?embed" allowfullscreen="allowfullscreen" allow="fullscreen">
  </iframe>
</div>

With some AI troubleshooting, the fix was deemed to be to put the <div> style attribute on a single line:

<div style="position: relative; width: 100%; height: 0; padding-top: 56.2500%; padding-bottom: 0; box-shadow: 0 2px 8px 0 rgba(63,69,81,0.16); margin-top: 1.6em; margin-bottom: 0.9em; overflow: hidden; border-radius: 8px; will-change: transform;">
  <iframe loading="lazy" title="2024 Year in Review Presentation" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: none; padding: 0;margin: 0;"
    src="https://www.canva.com/design/DAGbnweUoGY/-WaS6yuP3nwUW_lEH4TNKA/view?embed" allowfullscreen="allowfullscreen" allow="fullscreen">
  </iframe>
</div>

How We Verified the fix

I created a test script that runs content against multiple markdown2 versions:

"""Test a markdown string against multiple markdown2 versions."""

import subprocess
import sys

versions = ['2.5.5', '2.5.4', '2.5.3', '2.5.2', '2.5.1', '2.5.0', '2.4.13', '2.4.12', '2.4.11', '2.4.10']

# Multiline attribute on <div> - triggers the bug
failing_content = '''<div style="position: relative; width: 100%; height: 0; padding-top: 56.2500%;
 padding-bottom: 0; box-shadow: 0 2px 8px 0 rgba(63,69,81,0.16); margin-top: 1.6em; margin-bottom: 0.9em; overflow: hidden;
 border-radius: 8px; will-change: transform;">
  <iframe loading="lazy" title="2024 Year in Review Presentation" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: none; padding: 0;margin: 0;"
    src="https://www.canva.com/design/DAGbnweUoGY/-WaS6yuP3nwUW_lEH4TNKA/view?embed" allowfullscreen="allowfullscreen" allow="fullscreen">
  </iframe>
</div>'''

# Single-line attribute on <div> - the fix
fixed_content = '''<div style="position: relative; width: 100%; height: 0; padding-top: 56.2500%; padding-bottom: 0; box-shadow: 0 2px 8px 0 rgba(63,69,81,0.16); margin-top: 1.6em; margin-bottom: 0.9em; overflow: hidden; border-radius: 8px; will-change: transform;">
  <iframe loading="lazy" title="2024 Year in Review Presentation" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: none; padding: 0;margin: 0;"
    src="https://www.canva.com/design/DAGbnweUoGY/-WaS6yuP3nwUW_lEH4TNKA/view?embed" allowfullscreen="allowfullscreen" allow="fullscreen">
  </iframe>
</div>'''

for label, content in [("FAILING (multiline attribute)", failing_content), ("FIXED (single-line attribute)", fixed_content)]:
    print(f"=== {label} ===")
    for v in versions:
        r = subprocess.run(
            ["uv", "run", "--no-project", "--python", "3.13", "--with", f"markdown2=={v}",
             "python", "-c",
"""
import markdown2, sys
markdown2.markdown(sys.argv[1])
print('OK')
""", content],
            capture_output=True, text=True, timeout=30
        )
        out = r.stdout.strip()
        if not out:
            if 'AssertionError' in r.stderr:
                out = 'FAIL (AssertionError)'
            else:
                out = r.stderr.strip().split('\n')[-1][:100]
        print(f'{v}: {out}')
    print()

Results:

=== FAILING (multiline attribute) ===
2.5.5: FAIL (AssertionError)
2.5.4: OK
2.5.3: OK
2.5.2: OK
2.5.1: OK
2.5.0: OK
2.4.13: OK
2.4.12: OK
2.4.11: OK
2.4.10: OK

=== FIXED (single-line attribute) ===
2.5.5: OK
2.5.4: OK
2.5.3: OK
2.5.2: OK
2.5.1: OK
2.5.0: OK
2.4.13: OK
2.4.12: OK
2.4.11: OK
2.4.10: OK

I also tried this with a simple div with multiline styling and a p tag. I got the same issue, so I can rule out this is a problem with just iframes.

Conclusion

The bug was introduced in 2.5.5 — all prior versions handle multiline attributes without issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions