Skip to content

Commit e753c6a

Browse files
authored
bump pymdown-extensions to fix NoneType bug (#9319)
## 📝 Summary <!-- If this PR closes any issues, list them here by number (e.g., Closes #123). Detail the specific changes made in this pull request. Explain the problem addressed and how it was resolved. If applicable, provide before and after comparisons, screenshots, or any relevant details to help reviewers understand the changes easily. --> Closes #9316 mo.doc(mo.hstack) (and any other @mddoc-decorated callable) crashed with AttributeError: 'NoneType' object has no attribute 'replace' when resolved against pygments>=2.20 + pymdown-extensions<10.21.2. The root cause is upstream: pymdown-extensions <10.21.2 passes filename=None to pygments' HtmlFormatter, and pygments 2.20 stopped tolerating that. Fix released in [pymdown-extensions 10.21.2](http://facelessuser.github.io/pymdown-extensions/about/changelog/), so we bump our lower bound. ## 📋 Pre-Review Checklist <!-- These checks need to be completed before a PR is reviewed --> - [x] For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on [Discord](https://marimo.io/discord?ref=pr), or the community [discussions](https://github.com/marimo-team/marimo/discussions) (Please provide a link if applicable). - [ ] Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it. - [ ] Video or media evidence is provided for any visual changes (optional). <!-- PR is more likely to be merged if evidence is provided for changes made --> ## ✅ Merge Checklist - [x] I have read the [contributor guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md). - [ ] Documentation has been updated where applicable, including docstrings for API changes. - [x] Tests have been added for the changes made.
1 parent 8a1f565 commit e753c6a

3 files changed

Lines changed: 51 additions & 3 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ dependencies = [
1818
"markdown>=3.6,<4",
1919
# add features to markdown
2020
# Pinned to specific version for introduction of codeblock handling.
21-
"pymdown-extensions>=10.15,<11",
21+
"pymdown-extensions>=10.21.2,<11",
2222
# syntax highlighting of code in markdown
2323
"pygments>=2.19,<3",
2424
# for reading, writing configs
@@ -268,7 +268,7 @@ docs = [
268268
"pillow>=10.2.0,!=11.3.0", # for social cards, 11.3.0 doesn't have manylinux wheels
269269
"cairosvg>=2.7.1", # for social cards
270270
"mdx-include>=1.4.2",
271-
"pymdown-extensions>=10.7",
271+
"pymdown-extensions>=10.21.2",
272272
"lzstring>=1.0.4",
273273
"beautifulsoup4>=4.12.0",
274274
"markdownify>=0.13.0",

tests/_output/test_doc.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright 2026 Marimo. All rights reserved.
2+
"""Tests for `marimo._output.doc.doc` / `mo.doc`."""
3+
4+
from __future__ import annotations
5+
6+
import marimo as mo
7+
from marimo._output.doc import doc
8+
from marimo._output.hypertext import Html
9+
10+
11+
def test_doc_on_mddoc_decorated_function_does_not_raise() -> None:
12+
"""Regression test for https://github.com/marimo-team/marimo/issues/9316.
13+
14+
`mo.doc` renders `_rich_help_` output (which contains a ```python fenced
15+
code block with the object's signature) through pymdown-extensions +
16+
pygments. pymdown-extensions <10.21.2 passed `filename=None` to pygments'
17+
`HtmlFormatter`, and pygments >=2.20 crashed with
18+
`AttributeError: 'NoneType' object has no attribute 'replace'`.
19+
20+
This test exercises the full pipeline end-to-end on each `@mddoc`-decorated
21+
object used by the Layout tutorial so a regression in the pinned dependency
22+
range is caught immediately.
23+
"""
24+
for obj in (mo.hstack, mo.vstack, mo.accordion, mo.callout, mo.tree):
25+
result = doc(obj)
26+
assert isinstance(result, Html), (
27+
f"doc({obj.__name__}) returned {result!r}"
28+
)
29+
# The highlighted signature block should be present in the output.
30+
assert "codehilite" in result.text
31+
assert obj.__name__ in result.text
32+
33+
34+
def test_doc_on_mddoc_decorated_method_does_not_raise() -> None:
35+
for method in (mo.Html.center, mo.Html.right, mo.Html.left):
36+
result = doc(method)
37+
assert isinstance(result, Html)
38+
assert "codehilite" in result.text
39+
40+
41+
def test_doc_on_object_without_rich_help_returns_none() -> None:
42+
"""When the target object doesn't implement `_rich_help_`, `doc` should
43+
fall back to `help(obj)` and return `None`."""
44+
45+
class Plain:
46+
"""A plain class without `_rich_help_`."""
47+
48+
assert doc(Plain) is None

tests/snapshots/dependencies.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ narwhals>=2.0.0
99
packaging
1010
psutil>=5.0
1111
pygments>=2.19,<3
12-
pymdown-extensions>=10.15,<11
12+
pymdown-extensions>=10.21.2,<11
1313
pyyaml>=6.0.1
1414
pyzmq>=27.1.0; python_version < '3.15'
1515
starlette>=0.37.2

0 commit comments

Comments
 (0)