Skip to content

Commit 3a6bbb1

Browse files
authored
👌 IMPROVE: Substitution extension (#777)
Allow any value type (including dict, list, datetime, ...) and emit a `myst.substitution` warning for errors in resolving the substitution.
1 parent b043351 commit 3a6bbb1

File tree

9 files changed

+56
-11
lines changed

9 files changed

+56
-11
lines changed

myst_parser/config/dc_validators.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ def __call__(
4141
...
4242

4343

44+
def any_(inst, field, value, suffix=""):
45+
"""
46+
A validator that does not perform any validation.
47+
"""
48+
49+
4450
def instance_of(type_: type[Any] | tuple[type[Any], ...]) -> ValidatorType:
4551
"""
4652
A validator that raises a `TypeError` if the initializer is called

myst_parser/config/main.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from myst_parser.warnings_ import MystWarnings
2020
from .dc_validators import (
21+
any_,
2122
deep_iterable,
2223
deep_mapping,
2324
in_,
@@ -335,12 +336,10 @@ def __repr__(self) -> str:
335336

336337
# Extension specific
337338

338-
substitutions: Dict[str, Union[str, int, float]] = dc.field(
339+
substitutions: Dict[str, Any] = dc.field(
339340
default_factory=dict,
340341
metadata={
341-
"validator": deep_mapping(
342-
instance_of(str), instance_of((str, int, float)), instance_of(dict)
343-
),
342+
"validator": deep_mapping(instance_of(str), any_, instance_of(dict)),
344343
"merge_topmatter": True,
345344
"help": "Substitutions mapping",
346345
"extension": "substitutions",

myst_parser/mdit_to_docutils/base.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,11 +1854,12 @@ def render_substitution(self, token: SyntaxTreeNode, inline: bool) -> None:
18541854
variable_context
18551855
)
18561856
except Exception as error:
1857-
error_msg = self.reporter.error(
1857+
self.create_warning(
18581858
f"Substitution error:{error.__class__.__name__}: {error}",
1859+
MystWarnings.SUBSTITUTION,
18591860
line=position,
1861+
append_to=self.current_node,
18601862
)
1861-
self.current_node += [error_msg]
18621863
return
18631864

18641865
# handle circular references
@@ -1869,11 +1870,12 @@ def render_substitution(self, token: SyntaxTreeNode, inline: bool) -> None:
18691870
self.document.sub_references = getattr(self.document, "sub_references", set())
18701871
cyclic = references.intersection(self.document.sub_references)
18711872
if cyclic:
1872-
error_msg = self.reporter.error(
1873+
self.create_warning(
18731874
f"circular substitution reference: {cyclic}",
1875+
MystWarnings.SUBSTITUTION,
18741876
line=position,
1877+
append_to=self.current_node,
18751878
)
1876-
self.current_node += [error_msg]
18771879
return
18781880

18791881
# TODO improve error reporting;

myst_parser/warnings_.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class MystWarnings(Enum):
5959
"""HTML could not be parsed."""
6060
INVALID_ATTRIBUTE = "attribute"
6161
"""Invalid attribute value."""
62+
SUBSTITUTION = "substitution"
63+
"""Substitution could not be resolved."""
6264

6365

6466
def _is_suppressed_warning(

tests/test_renderers/fixtures/myst-config.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,26 @@ text
234234
text
235235
.
236236

237-
[substitutions] --myst-enable-extensions=substitution --myst-substitutions='{"a": "b", "c": "d"}'
237+
[substitutions] --myst-enable-extensions=substitution --myst-substitutions='{"a": "b", "c": "d", "e": "{{f}}", "f": "{{e}}"}'
238238
.
239-
{{a}} {{c}}
239+
{{a}} {{c}} {{x}} {{e}}
240240
.
241241
<document source="<string>">
242242
<paragraph>
243243
b
244244

245245
d
246+
247+
<system_message level="2" line="1" source="<string>" type="WARNING">
248+
<paragraph>
249+
Substitution error:UndefinedError: 'x' is undefined [myst.substitution]
250+
251+
<system_message level="2" line="3" source="<string>" type="WARNING">
252+
<paragraph>
253+
circular substitution reference: {'e'} [myst.substitution]
254+
255+
<string>:1: (WARNING/2) Substitution error:UndefinedError: 'x' is undefined [myst.substitution]
256+
<string>:3: (WARNING/2) circular substitution reference: {'e'} [myst.substitution]
246257
.
247258

248259
[attrs_image] --myst-enable-extensions=attrs_image

tests/test_renderers/fixtures/reporter_warnings.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ myst:
6262
.
6363
<string>:1: (WARNING/2) 'title_to_header' must be of type <class 'bool'> (got 1 that is a <class 'int'>). [myst.topmatter]
6464
<string>:1: (WARNING/2) 'url_schemes' is not a list of strings: [1] [myst.topmatter]
65-
<string>:1: (WARNING/2) 'substitutions['key']' must be of type (<class 'str'>, <class 'int'>, <class 'float'>) (got [] that is a <class 'list'>). [myst.topmatter]
6665
.
6766

6867
Bad HTML Meta

tests/test_sphinx/sourcedirs/substitutions/index.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ myst:
1818
Inline note
1919
```
2020
override: Overridden by front matter
21+
date: 2020-01-01
22+
nested_list:
23+
- item1
24+
nested_dict:
25+
key1: value1
2126

2227
---
2328

@@ -54,3 +59,9 @@ Using env and filters:
5459
```{toctree}
5560
other.md
5661
```
62+
63+
{{ date.strftime("%b %d, %Y") }}
64+
65+
{{ nested_list.0 }}
66+
67+
{{ nested_dict.key1 }}

tests/test_sphinx/test_sphinx_builds/test_substitutions.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@
7777
</li>
7878
</ul>
7979
</div>
80+
<p>
81+
Jan 01, 2020
82+
</p>
83+
<p>
84+
item1
85+
</p>
86+
<p>
87+
value1
88+
</p>
8089
</div>
8190
</div>
8291
</div>

tests/test_sphinx/test_sphinx_builds/test_substitutions.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,9 @@
5353
INDEX
5454
<compound classes="toctree-wrapper">
5555
<toctree caption="True" entries="(None,\ 'other')" glob="False" hidden="False" includefiles="other" includehidden="False" maxdepth="-1" numbered="0" parent="index" rawentries="" titlesonly="False">
56+
<paragraph>
57+
Jan 01, 2020
58+
<paragraph>
59+
item1
60+
<paragraph>
61+
value1

0 commit comments

Comments
 (0)