diff --git a/CHANGELOG.md b/CHANGELOG.md index 040ff138..4dd3b2fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* šŸ› Fix generator rule names being exhausted before all rule chains and unknown-name validation in `MarkdownIt.enable()` and `disable()`. + * ✨ Add `--enable-tables` to the CLI for file, standard input and interactive parsing in [#422](https://github.com/executablebooks/markdown-it-py/pull/422) * šŸ› Fix CLI interactive mode joining input lines with an extra newline, which split every line into its own paragraph and broke hard line breaks, in [#172](https://github.com/executablebooks/markdown-it-py/issues/172) * šŸ› Fix trimming and splitting with the Python whitespace set instead of the CommonMark one, which dropped U+001C–U+001F and U+0085 from paragraphs, headings, table cells and fence info strings and let distinct reference labels resolve each other, in [#418](https://github.com/executablebooks/markdown-it-py/pull/418), thanks to [@Nexory](https://github.com/Nexory) diff --git a/markdown_it/main.py b/markdown_it/main.py index 050a3b87..ed841cac 100644 --- a/markdown_it/main.py +++ b/markdown_it/main.py @@ -162,7 +162,7 @@ def enable( ) -> MarkdownIt: """Enable list or rules. (chainable) - :param names: rule name or list of rule names to enable. + :param names: rule name or iterable of rule names to enable. :param ignoreInvalid: set `true` to ignore errors when rule not found. It will automatically find appropriate components, @@ -176,8 +176,7 @@ def enable( """ result = [] - if isinstance(names, str): - names = [names] + names = [names] if isinstance(names, str) else list(names) for chain in ["core", "block", "inline"]: result.extend(self[chain].ruler.enable(names, True)) @@ -200,8 +199,7 @@ def disable( """ result = [] - if isinstance(names, str): - names = [names] + names = [names] if isinstance(names, str) else list(names) for chain in ["core", "block", "inline"]: result.extend(self[chain].ruler.disable(names, True)) diff --git a/tests/test_api/test_main.py b/tests/test_api/test_main.py index c84e33de..2676aa9b 100644 --- a/tests/test_api/test_main.py +++ b/tests/test_api/test_main.py @@ -544,3 +544,56 @@ def peek_rule(state, silent): # the rule saw the text accumulate one character at a time assert seen[:4] == [0, 1, 2, 3] assert max(seen) == 9_999 + + +def _check_rule_names_generator_applies_to_all_chains(method: str) -> None: + """An iterator must be reused across each rule chain.""" + md = MarkdownIt("zero" if method == "enable" else "commonmark") + core_rule = "smartquotes" if method == "enable" else "normalize" + names = (name for name in [core_rule, "heading", "emphasis"]) + assert getattr(md, method)(names) is md + active = md.get_active_rules() + for chain, name in [ + ("core", core_rule), + ("block", "heading"), + ("inline", "emphasis"), + ("inline2", "emphasis"), + ]: + assert (name in active[chain]) is (method == "enable") + + +def _check_rule_names_generator_reports_unknown_rules(method: str) -> None: + """Unknown names are checked after all chains have inspected an iterator.""" + with pytest.raises(ValueError, match="unknown rule"): + getattr(MarkdownIt(), method)(name for name in ["missing-rule"]) + + +def _check_rule_names_generator_ignores_unknown_rules(method: str) -> None: + """Ignoring an unknown name must still apply the known iterator names.""" + md = MarkdownIt("zero" if method == "enable" else "commonmark") + getattr(md, method)((name for name in ["missing-rule", "heading"]), True) + assert ("heading" in md.get_active_rules()["block"]) is (method == "enable") + + +def test_enable_generator_applies_to_all_chains() -> None: + _check_rule_names_generator_applies_to_all_chains("enable") + + +def test_disable_generator_applies_to_all_chains() -> None: + _check_rule_names_generator_applies_to_all_chains("disable") + + +def test_enable_generator_reports_unknown_rules() -> None: + _check_rule_names_generator_reports_unknown_rules("enable") + + +def test_disable_generator_reports_unknown_rules() -> None: + _check_rule_names_generator_reports_unknown_rules("disable") + + +def test_enable_generator_ignores_unknown_rules() -> None: + _check_rule_names_generator_ignores_unknown_rules("enable") + + +def test_disable_generator_ignores_unknown_rules() -> None: + _check_rule_names_generator_ignores_unknown_rules("disable")