Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ I think pip-based installation will enable this as well:
```shell
$ markdown2 foo.md > foo.html
```

Use `--use-file-vars` to enable extras declared in Emacs-style file variables,
such as `<!-- -*- markdown-extras: header-ids -*- -->` at the top of a file.
This flag takes no value; file variables are ignored by default.

```shell
$ markdown2 --use-file-vars foo.md > foo.html
```

See the [project wiki](https://github.com/trentm/python-markdown2/wiki),
[lib/markdown2.py](https://github.com/trentm/python-markdown2/blob/master/lib/markdown2.py)
docstrings and/or `python markdown2.py --help` for more details.
Expand Down
20 changes: 12 additions & 8 deletions lib/markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,7 @@ def __init__(
else:
self._toc_depth = self.extras["toc"].get("depth", 6)

if 'header-ids' in self.extras:
if not isinstance(self.extras['header-ids'], dict):
self.extras['header-ids'] = {
'mixed': False,
'prefix': self.extras['header-ids'],
'reset-count': True
}
self._normalize_header_ids()

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

def _normalize_header_ids(self):
if 'header-ids' in self.extras:
if not isinstance(self.extras['header-ids'], dict):
self.extras['header-ids'] = {
'mixed': False,
'prefix': self.extras['header-ids'],
'reset-count': True
}

def _setup_extras(self):
self._normalize_header_ids()
if "footnotes" in self.extras:
# order of insertion matters for footnotes. Use ordered dict for Python < 3.7
# https://docs.python.org/3/whatsnew/3.7.html#summary-release-highlights
Expand Down Expand Up @@ -4871,7 +4875,7 @@ def main(argv=None):
parser.add_argument("-x", "--extras", action="append",
help="Turn on specific extra features (not part of "
"the core Markdown spec). See above.")
parser.add_argument("--use-file-vars",
parser.add_argument("--use-file-vars", action="store_true",
help="Look for and use Emacs-style 'markdown-extras' "
"file var to turn on extras. See "
"<https://github.com/trentm/python-markdown2/wiki/Extras>")
Expand Down
66 changes: 66 additions & 0 deletions test/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Regression tests for the markdown2 command line interface."""

import subprocess
import sys
import tempfile
import unittest
from pathlib import Path


class FileVarsTestCase(unittest.TestCase):
source = "<!-- -*- markdown-extras: header-ids -*- -->\n# Heading\n"
script = Path(__file__).resolve().parent.parent / "lib" / "markdown2.py"

def run_cli(self, *args, text=""):
result = subprocess.run(
[sys.executable, str(self.script), *map(str, args)],
input=text,
text=True,
capture_output=True,
timeout=10,
)
self.assertEqual(result.returncode, 0, result.stderr)
return result.stdout

def test_file_vars_from_stdin(self):
html = self.run_cli("--use-file-vars", text=self.source)
self.assertIn('<h1 id="heading">Heading</h1>', html)

def test_file_vars_before_path(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "input.md"
path.write_text(self.source, encoding="utf-8")
html = self.run_cli("--use-file-vars", path)
self.assertIn('<h1 id="heading">Heading</h1>', html)

def test_file_vars_after_path(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "input.md"
path.write_text(self.source, encoding="utf-8")
html = self.run_cli(path, "--use-file-vars")
self.assertIn('<h1 id="heading">Heading</h1>', html)

def test_file_vars_with_multiple_paths(self):
with tempfile.TemporaryDirectory() as directory:
paths = [
Path(directory) / name for name in ("first.md", "second.md")
]
for path in paths:
path.write_text(self.source, encoding="utf-8")
html = self.run_cli("--use-file-vars", *paths)
self.assertEqual(html.count('<h1 id="heading">Heading</h1>'), 2)

def test_file_vars_with_output_option(self):
with tempfile.TemporaryDirectory() as directory:
output = Path(directory) / "output.html"
stdout = self.run_cli(
"--use-file-vars", "--output", output, text=self.source
)
self.assertEqual(stdout, "")
html = output.read_text(encoding="utf-8")
self.assertIn('<h1 id="heading">Heading</h1>', html)

def test_file_vars_are_disabled_by_default(self):
html = self.run_cli(text=self.source)
self.assertIn("<h1>Heading</h1>", html)
self.assertNotIn('id="heading"', html)
10 changes: 10 additions & 0 deletions test/test_markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ class DirectTestCase(_MarkdownTestCase):
Python-markdown (markdown.py).
"""

def test_header_ids_from_file_vars(self):
md = markdown2.Markdown(use_file_vars=True)
modeline = "<!-- -*- markdown-extras: header-ids{} -*- -->\n"
for option, heading_id in (
("", "heading"), ("=chapter", "chapter-heading")
):
html = md.convert(modeline.format(option) + "# Heading\n")
self.assertIn('<h1 id="{}">Heading</h1>'.format(heading_id), html)
self.assertEqual(md.convert("# Heading\n"), "<h1>Heading</h1>\n")

def test_slow_hr(self):
import time
text = """\
Expand Down