diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 9ae74fb1..6f0f3518 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -66,6 +66,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Improve Markdown extension error messages. (#782). * Drop official support for Python 3.3 and set `tornado>=5.0` (#1427). * Add support for GitLab edit links (#1435). * Link to GitHub issues from release notes (#644). diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index 30da2a2a..a67dd05e 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from collections import Sequence import os from collections import namedtuple +import markdown from mkdocs import utils, theme, plugins from mkdocs.config.base import Config, ValidationError @@ -629,7 +630,16 @@ class MarkdownExtensions(OptionallyRequired): extensions.append(item) else: raise ValidationError('Invalid Markdown Extensions configuration') - return utils.reduce_list(self.builtins + extensions) + + extensions = utils.reduce_list(self.builtins + extensions) + + # Confirm that Markdown considers extensions to be valid + try: + markdown.Markdown(extensions=extensions, extension_configs=self.configdata) + except Exception as e: + raise ValidationError(e.args[0]) + + return extensions def post_validation(self, config, key_name): config[self.configkey] = self.configdata diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index 56b7a724..1d573ba2 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import os import unittest +from mock import patch import mkdocs from mkdocs import utils @@ -491,7 +492,8 @@ class PrivateTest(unittest.TestCase): class MarkdownExtensionsTest(unittest.TestCase): - def test_simple_list(self): + @patch('markdown.Markdown') + def test_simple_list(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': ['foo', 'bar'] @@ -503,7 +505,8 @@ class MarkdownExtensionsTest(unittest.TestCase): 'mdx_configs': {} }, config) - def test_list_dicts(self): + @patch('markdown.Markdown') + def test_list_dicts(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -522,7 +525,8 @@ class MarkdownExtensionsTest(unittest.TestCase): } }, config) - def test_mixed_list(self): + @patch('markdown.Markdown') + def test_mixed_list(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -539,7 +543,8 @@ class MarkdownExtensionsTest(unittest.TestCase): } }, config) - def test_builtins(self): + @patch('markdown.Markdown') + def test_builtins(self, mockMd): option = config_options.MarkdownExtensions(builtins=['meta', 'toc']) config = { 'markdown_extensions': ['foo', 'bar'] @@ -577,7 +582,8 @@ class MarkdownExtensionsTest(unittest.TestCase): 'mdx_configs': {'toc': {'permalink': True}} }, config) - def test_configkey(self): + @patch('markdown.Markdown') + def test_configkey(self, mockMd): option = config_options.MarkdownExtensions(configkey='bar') config = { 'markdown_extensions': [ @@ -605,12 +611,14 @@ class MarkdownExtensionsTest(unittest.TestCase): 'mdx_configs': {} }, config) - def test_not_list(self): + @patch('markdown.Markdown') + def test_not_list(self, mockMd): option = config_options.MarkdownExtensions() self.assertRaises(config_options.ValidationError, option.validate, 'not a list') - def test_invalid_config_option(self): + @patch('markdown.Markdown') + def test_invalid_config_option(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -622,7 +630,8 @@ class MarkdownExtensionsTest(unittest.TestCase): option.validate, config['markdown_extensions'] ) - def test_invalid_config_item(self): + @patch('markdown.Markdown') + def test_invalid_config_item(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -634,7 +643,8 @@ class MarkdownExtensionsTest(unittest.TestCase): option.validate, config['markdown_extensions'] ) - def test_invalid_dict_item(self): + @patch('markdown.Markdown') + def test_invalid_dict_item(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -645,3 +655,13 @@ class MarkdownExtensionsTest(unittest.TestCase): config_options.ValidationError, option.validate, config['markdown_extensions'] ) + + def test_unknown_extension(self): + option = config_options.MarkdownExtensions() + config = { + 'markdown_extensions': ['unknown'] + } + self.assertRaises( + config_options.ValidationError, + option.validate, config['markdown_extensions'] + ) diff --git a/requirements/project-min.txt b/requirements/project-min.txt index 6b0262e0..a60bec24 100644 --- a/requirements/project-min.txt +++ b/requirements/project-min.txt @@ -4,3 +4,4 @@ livereload==2.5.1 Markdown==2.5 PyYAML==3.10 tornado==4.1 +mdx_gh_links>=0.2