diff --git a/myst_parser/config/main.py b/myst_parser/config/main.py index 24e2a540..4a2e646b 100644 --- a/myst_parser/config/main.py +++ b/myst_parser/config/main.py @@ -91,10 +91,9 @@ def check_url_schemes(inst: "MdParserConfig", field: dc.Field, value: Any) -> No raise TypeError( f"'{field.name}[{key}][title]' is not a string: {val['title']!r}" ) - if ( - "classes" in val - and not isinstance(val["classes"], list) - and not all(isinstance(c, str) for c in val["classes"]) + if "classes" in val and ( + not isinstance(val["classes"], list) + or not all(isinstance(c, str) for c in val["classes"]) ): raise TypeError( f"'{field.name}[{key}][classes]' is not a list of str: {val['classes']!r}" diff --git a/tests/test_config_topmatter.py b/tests/test_config_topmatter.py index 58be4461..af030a50 100644 --- a/tests/test_config_topmatter.py +++ b/tests/test_config_topmatter.py @@ -225,6 +225,29 @@ def test_the_dict_form_keeps_its_templates(self): assert warnings == [] assert config.url_schemes == {"wiki": {"url": "https://w.org/{{path}}"}} + @pytest.mark.parametrize( + "classes", + ["my-class", ["a", 1], 1], + ids=["string", "non-string-item", "int"], + ) + def test_classes_must_be_a_list_of_strings(self, classes): + """A bare string was accepted, and split into one class per character; + a list holding a non-string was accepted, and crashed the HTML writer. + """ + with pytest.raises(TypeError, match=r"\[classes\]' is not a list of str"): + MdParserConfig(url_schemes={"gh": {"url": "{{path}}", "classes": classes}}) + _config, warnings = merge( + {"myst": {"url_schemes": {"gh": {"url": "{{path}}", "classes": classes}}}} + ) + assert len(warnings) == 1 + assert "is not a list of str" in warnings[0] + + def test_classes_list_of_strings_is_accepted(self): + config = MdParserConfig( + url_schemes={"gh": {"url": "{{path}}", "classes": ["a", "b"]}} + ) + assert config.url_schemes["gh"]["classes"] == ["a", "b"] + class TestGlobalOnlyWarningKind: """The global-only rejection is a ``myst.topmatter`` warning.