diff --git a/docs/changelog.md b/docs/changelog.md index 7926ab4a..043bfa2d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,10 @@ See the [Contributing Guide](contributing.md) for details. ### Fixed +* The `toc` extension no longer raises `TypeError` for a heading whose `id` + attribute is present but valueless, as the `attr_list` extension's + bare-attribute syntax (`{id}`) writes it: like a heading with no `id` at + all, it now gets a generated slug (#1705). * Fix for complex cases in new emphasis handling where text could be unexpectedly dropped (#1694). * Fix rule of 3 handling in new emphasis handling where an opener skipped by the rule could still be paired diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index ba65225c..261f2af0 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -400,7 +400,10 @@ def run(self, doc: etree.Element) -> None: name = strip_tags(innerhtml) # Do not override pre-existing ids - if "id" not in el.attrib: + if not el.attrib.get("id"): + # The `attr_list` extension writes bare attributes with a + # value of None. A valueless id is no id at all, so a slug + # is generated for it like for any other id-less heading. el.attrib["id"] = unique(self.slugify(html.unescape(name), self.sep), used_ids, id_counters) data_toc_label = '' diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py index 12a07d50..60fa87da 100644 --- a/tests/test_syntax/extensions/test_toc.py +++ b/tests/test_syntax/extensions/test_toc.py @@ -22,6 +22,7 @@ from markdown.test_tools import TestCase from markdown import Markdown from markdown.extensions.toc import TocExtension, strip_tags, unique +from markdown.extensions.attr_list import AttrListExtension from markdown.extensions.nl2br import Nl2BrExtension @@ -1574,3 +1575,39 @@ def testDontStripHTMLEntities(self): strip_tags('foo < & < bar'), 'foo < & < bar' ) + + +class TestTOCWithAttrList(TestCase): + """The `toc` extension together with `attr_list` (#1705).""" + + maxDiff = None + default_kwargs = { + 'extensions': [TocExtension(), AttrListExtension()] + } + + def testBareIdAttribute(self): + # The `attr_list` extension writes bare attributes with a value of + # None. A valueless `id` is no id at all, so `toc` generates a slug + # for it like for any other id-less heading instead of raising a + # `TypeError`. + self.assertMarkdownRenders( + '#d {id}', + '