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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## Unreleased

* ✨ Add `--enable-tables` to the CLI for file, standard input and interactive parsing.

## 4.2.0 - 2026-05-07

* ✨ Add `make_fence_rule()` factory for configurable fence markers in [#394](https://github.com/executablebooks/markdown-it-py/pull/394)
Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,18 @@ Render markdown to HTML with markdown-it-py from the
command-line:

```console
usage: markdown-it [-h] [-v] [--stdin|filenames [filenames ...]]
usage: markdown-it [-h] [-v] [--stdin] [--enable-tables] [filenames ...]

Parse one or more markdown files, convert each to HTML, and print to stdout

positional arguments:
--stdin read source Markdown file from standard input
filenames specify an optional list of files to convert
filenames specify an optional list of files to convert

optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
options:
-h, --help show this help message and exit
-v, --version show program's version number and exit
--stdin read Markdown from standard input
--enable-tables enable table parsing

Interactive:

Expand All @@ -132,6 +133,15 @@ Batch:

```

Tables are disabled by default, as in CommonMark.
Use `--enable-tables` with any input mode to enable them:

```bash
markdown-it --enable-tables README.md > index.html
markdown-it --enable-tables --stdin < README.md > index.html
markdown-it --enable-tables
```

## References / Thanks

Big thanks to the authors of [markdown-it]:
Expand Down
29 changes: 18 additions & 11 deletions markdown_it/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,54 @@

def main(args: Sequence[str] | None = None) -> int:
namespace = parse_args(args)
md = MarkdownIt()
if namespace.enable_tables:
md.enable("table")
if namespace.filenames:
convert(namespace.filenames)
convert(namespace.filenames, md)
elif namespace.stdin:
convert_stdin()
convert_stdin(md)
else:
interactive()
interactive(md)
return 0


def convert(filenames: Iterable[str]) -> None:
def convert(filenames: Iterable[str], md: MarkdownIt | None = None) -> None:
for filename in filenames:
convert_file(filename)
convert_file(filename, md)


def convert_stdin() -> None:
def convert_stdin(md: MarkdownIt | None = None) -> None:
"""
Parse a Markdown file and dump the output to stdout.
"""
try:
rendered = MarkdownIt().render(sys.stdin.read())
rendered = (md or MarkdownIt()).render(sys.stdin.read())
print(rendered, end="")
except OSError:
sys.stderr.write("Cannot parse Markdown from the standard input.\n")
sys.exit(1)


def convert_file(filename: str) -> None:
def convert_file(filename: str, md: MarkdownIt | None = None) -> None:
"""
Parse a Markdown file and dump the output to stdout.
"""
try:
with open(filename, encoding="utf8", errors="ignore") as fin:
rendered = MarkdownIt().render(fin.read())
rendered = (md or MarkdownIt()).render(fin.read())
print(rendered, end="")
except OSError:
sys.stderr.write(f'Cannot open file "{filename}".\n')
sys.exit(1)


def interactive() -> None:
def interactive(md: MarkdownIt | None = None) -> None:
"""
Parse user input, dump to stdout, rinse and repeat.
Python REPL style.
"""
md = md or MarkdownIt()
print_heading()
contents = []
more = False
Expand All @@ -71,7 +75,7 @@ def interactive() -> None:
prompt, more = ("... ", True) if more else (">>> ", True)
contents.append(input(prompt) + "\n")
except EOFError:
print("\n" + MarkdownIt().render("\n".join(contents)), end="")
print("\n" + md.render("".join(contents)), end="")
more = False
contents = []
except KeyboardInterrupt:
Expand Down Expand Up @@ -111,6 +115,9 @@ def parse_args(args: Sequence[str] | None) -> argparse.Namespace:
parser.add_argument(
"--stdin", action="store_true", help="read Markdown from standard input"
)
parser.add_argument(
"--enable-tables", action="store_true", help="enable table parsing"
)
parser.add_argument(
"filenames", nargs="*", help="specify an optional list of files to convert"
)
Expand Down
34 changes: 34 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,37 @@ def test_interactive_render():
# The rendered output is prefixed by a newline
assert "\n<h1>hello</h1>\n" in output
assert "Exiting" in output


@pytest.mark.parametrize("route", ["files", "stdin", "interactive"])
@pytest.mark.parametrize("enable_tables", [False, True])
def test_tables(route, enable_tables, tmp_path, capsys):
"""Table parsing is opt-in on every CLI route, preserving HTML settings."""
source = "a | b\n--- | ---\n1 | 2\n\n<em>raw</em> ~~plain~~\n"
expected = (
"<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n"
"<tbody>\n<tr>\n<td>1</td>\n<td>2</td>\n</tr>\n</tbody>\n</table>\n"
if enable_tables
else "<p>a | b\n--- | ---\n1 | 2</p>\n"
) + "<p><em>raw</em> ~~plain~~</p>\n"
args = ["--enable-tables"] if enable_tables else []
if route == "files":
paths = [tmp_path / "first.md", tmp_path / "second.md"]
for path in paths:
path.write_text(source, encoding="utf8")
assert parse.main([*args, *map(str, paths)]) == 0
expected *= 2
elif route == "stdin":
with patch("sys.stdin", io.StringIO(source)):
assert parse.main([*args, "--stdin"]) == 0
else:
inputs = [*source.splitlines(), EOFError] * 2 + [KeyboardInterrupt]
with patch("builtins.input", side_effect=inputs):
assert parse.main(args) == 0
expected = (
f"{parse.version_str} (interactive)\n"
"Type Ctrl-D to complete input, or Ctrl-C to exit.\n"
+ ("\n" + expected) * 2
+ "\nExiting.\n"
)
assert capsys.readouterr().out == expected