Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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