diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst index d1912676a..d16f3a597 100644 --- a/doc/extdev/appapi.rst +++ b/doc/extdev/appapi.rst @@ -336,6 +336,12 @@ package. .. versionadded:: 1.1 +.. method:: Sphinx.add_source_parser(name, suffix, parser) + + Register a parser class for specified *suffix*. + + .. versionadded:: 1.4 + .. method:: Sphinx.require_sphinx(version) Compare *version* (which must be a ``major.minor`` version string, diff --git a/sphinx/application.py b/sphinx/application.py index 860ea2378..3842b13fa 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -77,6 +77,7 @@ class Sphinx(object): self.next_listener_id = 0 self._extensions = {} self._extension_metadata = {} + self._additional_source_parsers = {} self._listeners = {} self._setting_up_extension = ['?'] self.domains = BUILTIN_DOMAINS.copy() @@ -185,6 +186,8 @@ class Sphinx(object): self._init_i18n() # check all configuration values for permissible types self.config.check_types(self.warn) + # set up source_parsers + self._init_source_parsers() # set up the build environment self._init_env(freshenv) # set up the builder @@ -211,6 +214,13 @@ class Sphinx(object): else: self.info('not available for built-in messages') + def _init_source_parsers(self): + for suffix, parser in iteritems(self._additional_source_parsers): + if suffix not in self.config.source_suffix: + self.config.source_suffix.append(suffix) + if suffix not in self.config.source_parsers: + self.config.source_parsers[suffix] = parser + def _init_env(self, freshenv): if freshenv: self.env = BuildEnvironment(self.srcdir, self.doctreedir, @@ -752,6 +762,10 @@ class Sphinx(object): assert issubclass(cls, SearchLanguage) languages[cls.lang] = cls + def add_source_parser(self, suffix, parser): + self.debug('[app] adding search source_parser: %r, %r', (suffix, parser)) + self._additional_source_parsers[suffix] = parser + class TemplateBridge(object): """ diff --git a/tests/roots/test-add_source_parser-conflicts-with-users-setting/conf.py b/tests/roots/test-add_source_parser-conflicts-with-users-setting/conf.py new file mode 100644 index 000000000..db9fe54a9 --- /dev/null +++ b/tests/roots/test-add_source_parser-conflicts-with-users-setting/conf.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +import os +import sys +from docutils.parsers import Parser + +sys.path.insert(0, os.path.abspath('.')) + + +class DummyTestParser(Parser): + pass + + +extensions = ['test_source_parser'] +source_suffix = ['.rst', '.test'] +source_parsers = { + '.test': DummyTestParser +} diff --git a/tests/roots/test-add_source_parser-conflicts-with-users-setting/test_source_parser.py b/tests/roots/test-add_source_parser-conflicts-with-users-setting/test_source_parser.py new file mode 100644 index 000000000..0dff7e311 --- /dev/null +++ b/tests/roots/test-add_source_parser-conflicts-with-users-setting/test_source_parser.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from docutils.parsers import Parser + + +class TestSourceParser(Parser): + pass + + +def setup(app): + app.add_source_parser('.test', TestSourceParser) diff --git a/tests/roots/test-add_source_parser/conf.py b/tests/roots/test-add_source_parser/conf.py new file mode 100644 index 000000000..f9969341a --- /dev/null +++ b/tests/roots/test-add_source_parser/conf.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +import os +import sys +from docutils.parsers import Parser + +sys.path.insert(0, os.path.abspath('.')) + + +class DummyMarkdownParser(Parser): + pass + + +extensions = ['test_source_parser'] +source_suffix = ['.rst', '.md'] +source_parsers = { + '.md': DummyMarkdownParser +} diff --git a/tests/roots/test-add_source_parser/test_source_parser.py b/tests/roots/test-add_source_parser/test_source_parser.py new file mode 100644 index 000000000..0dff7e311 --- /dev/null +++ b/tests/roots/test-add_source_parser/test_source_parser.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from docutils.parsers import Parser + + +class TestSourceParser(Parser): + pass + + +def setup(app): + app.add_source_parser('.test', TestSourceParser) diff --git a/tests/test_application.py b/tests/test_application.py index 3a3ee6728..7bc970c9d 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -88,3 +88,18 @@ def test_domain_override(app, status, warning): assert app.override_domain(B) is None raises_msg(ExtensionError, 'new domain not a subclass of registered ' 'foo domain', app.override_domain, C) + + +@with_app(testroot='add_source_parser') +def test_add_source_parser(app, status, warning): + assert set(app.config.source_suffix) == set(['.rst', '.md', '.test']) + assert set(app.config.source_parsers.keys()) == set(['.md', '.test']) + assert app.config.source_parsers['.md'].__name__ == 'DummyMarkdownParser' + assert app.config.source_parsers['.test'].__name__ == 'TestSourceParser' + + +@with_app(testroot='add_source_parser-conflicts-with-users-setting') +def test_add_source_parser_conflicts_with_users_setting(app, status, warning): + assert set(app.config.source_suffix) == set(['.rst', '.test']) + assert set(app.config.source_parsers.keys()) == set(['.test']) + assert app.config.source_parsers['.test'].__name__ == 'DummyTestParser'