|
1 | 1 | import contextlib |
2 | 2 | import importlib.util |
3 | 3 | import io |
| 4 | +import sys |
4 | 5 | from dataclasses import dataclass, field, fields |
5 | 6 | from textwrap import dedent |
6 | 7 | from typing import Literal |
@@ -210,6 +211,89 @@ def test_linkify_no_warning_when_available(): |
210 | 211 | assert "[myst.linkify]" not in stream.getvalue() |
211 | 212 |
|
212 | 213 |
|
| 214 | +def test_html_deep_nesting_warns(): |
| 215 | + """Deeply nested HTML degrades to raw output with a warning. |
| 216 | +
|
| 217 | + Regression: rendering the HTML AST recurses once per nesting level, |
| 218 | + and the resulting ``RecursionError`` escaped and aborted the build. |
| 219 | + """ |
| 220 | + depth = sys.getrecursionlimit() |
| 221 | + # tags on separate lines, to stay under docutils' line-length-limit |
| 222 | + source = ( |
| 223 | + '<div class="admonition"><p class="title">Title</p>\n' |
| 224 | + + "<div>\n" * depth |
| 225 | + + "content\n" |
| 226 | + + "</div>\n" * depth |
| 227 | + + "</div>\n" |
| 228 | + ) |
| 229 | + stream = io.StringIO() |
| 230 | + doctree = publish_doctree( |
| 231 | + source=source, |
| 232 | + parser=Parser(), |
| 233 | + settings_overrides={ |
| 234 | + "myst_enable_extensions": ["html_admonition"], |
| 235 | + "warning_stream": stream, |
| 236 | + }, |
| 237 | + ) |
| 238 | + assert "HTML is too deeply nested" in stream.getvalue() |
| 239 | + assert "[myst.html]" in stream.getvalue() |
| 240 | + # the original text is preserved as a raw HTML node |
| 241 | + assert list(doctree.findall(nodes.raw)) |
| 242 | + |
| 243 | + |
| 244 | +@pytest.mark.parametrize( |
| 245 | + "yaml_line", |
| 246 | + [ |
| 247 | + "title: !UnknownTag value", # yaml.constructor.ConstructorError |
| 248 | + "date: 2021-99-99", # bare ValueError from an out-of-range timestamp |
| 249 | + ], |
| 250 | +) |
| 251 | +def test_topmatter_hostile_yaml_warns(yaml_line): |
| 252 | + """Front matter YAML errors beyond parser/scanner ones warn, not crash. |
| 253 | +
|
| 254 | + Regression: only ``ParserError``/``ScannerError`` were caught, so e.g. a |
| 255 | + ``ConstructorError`` from an unknown tag aborted the whole build. |
| 256 | + """ |
| 257 | + stream = io.StringIO() |
| 258 | + doctree = publish_doctree( |
| 259 | + source=f"---\n{yaml_line}\n---\n\ncontent\n", |
| 260 | + parser=Parser(), |
| 261 | + settings_overrides={"warning_stream": stream}, |
| 262 | + ) |
| 263 | + assert "[myst.topmatter]" in stream.getvalue() |
| 264 | + assert "content" in doctree.pformat() |
| 265 | + |
| 266 | + |
| 267 | +def test_footnote_label_matching_heading_name(): |
| 268 | + """A footnote label sharing a heading's name is not a duplicate. |
| 269 | +
|
| 270 | + Regression: the duplicate check used ``document.nameids``, which holds |
| 271 | + every target name, so the footnote definition was silently dropped. |
| 272 | + """ |
| 273 | + stream = io.StringIO() |
| 274 | + doctree = publish_doctree( |
| 275 | + source="# Note\n\n[^note]\n\n[^note]: the definition\n", |
| 276 | + parser=Parser(), |
| 277 | + settings_overrides={"warning_stream": stream}, |
| 278 | + ) |
| 279 | + footnotes = list(doctree.findall(nodes.footnote)) |
| 280 | + assert footnotes, "expected the footnote definition to be kept" |
| 281 | + assert "the definition" in footnotes[0].astext() |
| 282 | + assert "Duplicate footnote" not in stream.getvalue() |
| 283 | + |
| 284 | + |
| 285 | +def test_footnote_duplicate_definition_warns(): |
| 286 | + """A genuinely duplicated footnote definition still warns and is dropped.""" |
| 287 | + stream = io.StringIO() |
| 288 | + doctree = publish_doctree( |
| 289 | + source="[^a]\n\n[^a]: first\n\n[^a]: second\n", |
| 290 | + parser=Parser(), |
| 291 | + settings_overrides={"warning_stream": stream}, |
| 292 | + ) |
| 293 | + assert "Duplicate footnote definition" in stream.getvalue() |
| 294 | + assert len(list(doctree.findall(nodes.footnote))) == 1 |
| 295 | + |
| 296 | + |
213 | 297 | def test_definition_list_orphan_definition(): |
214 | 298 | """A definition with no preceding term errors, but keeps its content. |
215 | 299 |
|
|
0 commit comments