From e57e2269d911c16cb8eb0598460cd50e6569a79f Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 6 Apr 2018 13:30:47 -0400 Subject: [PATCH] Improve Markdown extension error messages. Fixes #782. Note that we mock Markdown in the tests to ensure those tests are not using Markdown to validate the extension names. We don't mock Markdown in the tests which we do want Markdown to validate the extension names. Also ensure project-min has all relevant extensions. --- docs/about/release-notes.md | 1 + mkdocs/config/config_options.py | 12 ++++++- mkdocs/tests/config/config_options_tests.py | 38 ++++++++++++++++----- requirements/project-min.txt | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) 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