Skip to content

Fix toc crash on a heading with a valueless id attribute - #1706

Open
rahul05ranjan wants to merge 3 commits into
Python-Markdown:masterfrom
rahul05ranjan:fix/toc-valueless-id
Open

rahul05ranjan wants to merge 3 commits into
Python-Markdown:masterfrom
rahul05ranjan:fix/toc-valueless-id

Conversation

@rahul05ranjan

@rahul05ranjan rahul05ranjan commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes #1705. The attr_list extension writes a bare attribute like {id} with a None value, and the toc extension's "do not override existing ids" check only tested key membership — so a heading with a valueless id skipped slug generation and later crashed in re.sub. The fix treats a falsy id the same as a missing one and generates a slug.

AI Assistance Disclosure

  • AI tools were used and I have disclosed which ones, and have fully reviewed and verified their output.

    AI tools were used to assist with the following:

    • Code
    • Code Comments
    • Documentation
    • Changelog Entry
    • Commit Message
    • The Description Above
    • Other: ______________

Checklist

The attr_list extension writes bare attributes (``{id}``) with a value
of None. The toc extension's "do not override pre-existing ids" check
only tested key membership, so a heading whose id attribute existed but
carried no value skipped slug generation and later passed the None to
re.sub through unescape(), raising an unhandled TypeError:

    markdown.markdown("#d {id}", extensions=[attr_list, toc])
    TypeError: expected string or bytes-like object, got 'NoneType'

A valueless id is no id at all, so toc now treats a falsy id like a
missing one and generates a slug for it, exactly as for any other
id-less heading.

Fixes Python-Markdown#1705

Co-authored-by: rahul05ranjan <[email protected]>
@waylan

waylan commented Nov 12, 2026

Copy link
Copy Markdown
Member

As per our Contributing Guidelines:

Issue and pull request templates must be followed when provided. If a template is ignored or deleted, it will be assumed that an AI agent was used to submit the pull request or issue autonomously and the pull request or issue may be closed immediately.

I know you included the following in your comments:

Per the contributing guide's AI policy: I used AI to assist with this change and can explain every part of it.

But that doesn't cover everything we ask for in our template. Also, if you can explain everything, then why not explain it yourself instead of dumping AI output and making me read through a bunch of unnecessary stuff? For example, the "What's Wrong" section is completely unnecessary because you already indicated that this "fixes #1705" (the problem has already been defined). The first sentence under "Fix" would have been sufficient (the tests in your patch provide a demonstration, so the example is unnecessary). And the "Verification:" section provides little value once all of the CI checks run here. In the end, one or two sentences handwritten by a human would be much more likely to encourage me to take the time to review this PR. Instead, the entire experience is just annoying to me.

As the Contributing Guide notes (emphasis added):

Contributors are expected to actively engage with reviewer feedback themselves. Contributors are expected to understand the changes they are proposing, not act as a message courier for a third party. Copying and pasting replies to / from a generative AI tool does not count as engaging with the reviewer.

@waylan waylan added the more-info-needed More information needs to be provided. label Nov 12, 2026
The pyspelling lint reads comments and docstrings as plain words, so the
"attr_list" name in the new toc.py comment and the "TypeError" mentioned
in the new test docstring tripped the checkspelling job.
@rahul05ranjan

Copy link
Copy Markdown
Contributor Author

You're right, and I've redone the description: it's now the two sentences that matter, laid out on the PR template you linked, with the template's checkboxes filled in as they apply. The "What's wrong" example and the verification play-by-play are gone.

I've also fixed the one failing check — the spelling lint read the new comments as prose and flagged attr (from attr_list) and TypeError, so both are now in .spell-dict — and confirmed the tests pass locally (python -m unittest tests.test_syntax.extensions.test_toc on Python 3.11, all green).

Comment thread .spell-dict

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.

Actually, TypeError is code and should be in a codespan, which the spellchecker will ignore. I didn't check now attr is being used, but if it is code, then the same rule applies. If it is an abbreviation, then we do not use abbreviations, but fully spell out words, which is noted in our style guides in the Contributing Guide.

"TypeError" is code and "attr_list" is the name of an extension, so
per the style guides they belong in code spans (which the spellchecker
ignores) rather than in .spell-dict. Drop the dictionary entries and
wrap the words in code spans in the affected comments and docstrings.

Co-authored-by: rahul05ranjan <[email protected]>
@rahul05ranjan

Copy link
Copy Markdown
Contributor Author

You're right — the dictionary entries were the wrong tool. TypeError is code, and attr_list is the name of the extension, so both belong in code spans (which the spellchecker ignores), not in .spell-dict.

Fixed in 821ae37: the .spell-dict entries are removed, and TypeError and attr_list are now wrapped in code spans in the affected comments and docstrings. Verified the checkspelling config locally (pyspelling python and markdown matrices both pass without the dictionary entries) and the toc tests are unchanged and green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

more-info-needed More information needs to be provided.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

toc + attr_list: bare {id} attribute crashes toc with TypeError (unescape(None))

2 participants