Skip to content

Commit affc0d4

Browse files
Merge pull request #723 from ryanchou1994/fix/use-file-vars-flag
Fix file-variable CLI flag and header ID setup
2 parents 2962ae7 + 2cc5924 commit affc0d4

4 files changed

Lines changed: 97 additions & 8 deletions

File tree

‎README.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ I think pip-based installation will enable this as well:
7171
```shell
7272
$ markdown2 foo.md > foo.html
7373
```
74+
75+
Use `--use-file-vars` to enable extras declared in Emacs-style file variables,
76+
such as `<!-- -*- markdown-extras: header-ids -*- -->` at the top of a file.
77+
This flag takes no value; file variables are ignored by default.
78+
79+
```shell
80+
$ markdown2 --use-file-vars foo.md > foo.html
81+
```
82+
7483
See the [project wiki](https://github.com/trentm/python-markdown2/wiki),
7584
[lib/markdown2.py](https://github.com/trentm/python-markdown2/blob/master/lib/markdown2.py)
7685
docstrings and/or `python markdown2.py --help` for more details.

‎lib/markdown2.py‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,7 @@ def __init__(
360360
else:
361361
self._toc_depth = self.extras["toc"].get("depth", 6)
362362

363-
if 'header-ids' in self.extras:
364-
if not isinstance(self.extras['header-ids'], dict):
365-
self.extras['header-ids'] = {
366-
'mixed': False,
367-
'prefix': self.extras['header-ids'],
368-
'reset-count': True
369-
}
363+
self._normalize_header_ids()
370364

371365
if 'break-on-newline' in self.extras:
372366
# `break-on-newline` is an alias for the breaks extra's `on_newline`
@@ -412,7 +406,17 @@ def reset(self):
412406
self._setup_extras()
413407
self._toc = []
414408

409+
def _normalize_header_ids(self):
410+
if 'header-ids' in self.extras:
411+
if not isinstance(self.extras['header-ids'], dict):
412+
self.extras['header-ids'] = {
413+
'mixed': False,
414+
'prefix': self.extras['header-ids'],
415+
'reset-count': True
416+
}
417+
415418
def _setup_extras(self):
419+
self._normalize_header_ids()
416420
if "footnotes" in self.extras:
417421
# order of insertion matters for footnotes. Use ordered dict for Python < 3.7
418422
# https://docs.python.org/3/whatsnew/3.7.html#summary-release-highlights
@@ -4883,7 +4887,7 @@ def main(argv=None):
48834887
parser.add_argument("-x", "--extras", action="append",
48844888
help="Turn on specific extra features (not part of "
48854889
"the core Markdown spec). See above.")
4886-
parser.add_argument("--use-file-vars",
4890+
parser.add_argument("--use-file-vars", action="store_true",
48874891
help="Look for and use Emacs-style 'markdown-extras' "
48884892
"file var to turn on extras. See "
48894893
"<https://github.com/trentm/python-markdown2/wiki/Extras>")

‎test/test_cli.py‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Regression tests for the markdown2 command line interface."""
2+
3+
import subprocess
4+
import sys
5+
import tempfile
6+
import unittest
7+
from pathlib import Path
8+
9+
10+
class FileVarsTestCase(unittest.TestCase):
11+
source = "<!-- -*- markdown-extras: header-ids -*- -->\n# Heading\n"
12+
script = Path(__file__).resolve().parent.parent / "lib" / "markdown2.py"
13+
14+
def run_cli(self, *args, text=""):
15+
result = subprocess.run(
16+
[sys.executable, str(self.script), *map(str, args)],
17+
input=text,
18+
text=True,
19+
capture_output=True,
20+
timeout=10,
21+
)
22+
self.assertEqual(result.returncode, 0, result.stderr)
23+
return result.stdout
24+
25+
def test_file_vars_from_stdin(self):
26+
html = self.run_cli("--use-file-vars", text=self.source)
27+
self.assertIn('<h1 id="heading">Heading</h1>', html)
28+
29+
def test_file_vars_before_path(self):
30+
with tempfile.TemporaryDirectory() as directory:
31+
path = Path(directory) / "input.md"
32+
path.write_text(self.source, encoding="utf-8")
33+
html = self.run_cli("--use-file-vars", path)
34+
self.assertIn('<h1 id="heading">Heading</h1>', html)
35+
36+
def test_file_vars_after_path(self):
37+
with tempfile.TemporaryDirectory() as directory:
38+
path = Path(directory) / "input.md"
39+
path.write_text(self.source, encoding="utf-8")
40+
html = self.run_cli(path, "--use-file-vars")
41+
self.assertIn('<h1 id="heading">Heading</h1>', html)
42+
43+
def test_file_vars_with_multiple_paths(self):
44+
with tempfile.TemporaryDirectory() as directory:
45+
paths = [
46+
Path(directory) / name for name in ("first.md", "second.md")
47+
]
48+
for path in paths:
49+
path.write_text(self.source, encoding="utf-8")
50+
html = self.run_cli("--use-file-vars", *paths)
51+
self.assertEqual(html.count('<h1 id="heading">Heading</h1>'), 2)
52+
53+
def test_file_vars_with_output_option(self):
54+
with tempfile.TemporaryDirectory() as directory:
55+
output = Path(directory) / "output.html"
56+
stdout = self.run_cli(
57+
"--use-file-vars", "--output", output, text=self.source
58+
)
59+
self.assertEqual(stdout, "")
60+
html = output.read_text(encoding="utf-8")
61+
self.assertIn('<h1 id="heading">Heading</h1>', html)
62+
63+
def test_file_vars_are_disabled_by_default(self):
64+
html = self.run_cli(text=self.source)
65+
self.assertIn("<h1>Heading</h1>", html)
66+
self.assertNotIn('id="heading"', html)

‎test/test_markdown2.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,16 @@ class DirectTestCase(_MarkdownTestCase):
220220
Python-markdown (markdown.py).
221221
"""
222222

223+
def test_header_ids_from_file_vars(self):
224+
md = markdown2.Markdown(use_file_vars=True)
225+
modeline = "<!-- -*- markdown-extras: header-ids{} -*- -->\n"
226+
for option, heading_id in (
227+
("", "heading"), ("=chapter", "chapter-heading")
228+
):
229+
html = md.convert(modeline.format(option) + "# Heading\n")
230+
self.assertIn('<h1 id="{}">Heading</h1>'.format(heading_id), html)
231+
self.assertEqual(md.convert("# Heading\n"), "<h1>Heading</h1>\n")
232+
223233
def test_many_distinct_code_spans(self):
224234
source = '\n\n'.join('`value_%s`' % i for i in range(1000))
225235
expected = '\n\n'.join('<p><code>value_%s</code></p>' % i for i in range(1000)) + '\n'

0 commit comments

Comments
 (0)