diff --git a/sphinx/environment.py b/sphinx/environment.py index e2d446acc..02b041050 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -29,7 +29,7 @@ from docutils import nodes from docutils.io import NullOutput from docutils.core import Publisher from docutils.utils import Reporter, relative_path, get_source_line -from docutils.parsers.rst import roles, directives +from docutils.parsers.rst import roles from docutils.parsers.rst.languages import en as english from docutils.frontend import OptionParser @@ -43,6 +43,7 @@ from sphinx.util.images import guess_mimetype from sphinx.util.i18n import find_catalog_files, get_image_filename_for_language, \ search_image_for_language from sphinx.util.console import bold, purple +from sphinx.util.docutils import sphinx_domains from sphinx.util.matching import compile_matchers from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks from sphinx.util.websupport import is_commentable @@ -51,13 +52,6 @@ from sphinx.locale import _ from sphinx.versioning import add_uids, merge_doctrees from sphinx.transforms import SphinxContentsFilter -orig_role_function = roles.role -orig_directive_function = directives.directive - - -class ElementLookupError(Exception): - pass - default_settings = { 'embed_stylesheet': False, @@ -634,51 +628,6 @@ class BuildEnvironment(object): error.object[error.end:lineend]), lineno) return (u'?', error.end) - def lookup_domain_element(self, type, name): - """Lookup a markup element (directive or role), given its name which can - be a full name (with domain). - """ - name = name.lower() - # explicit domain given? - if ':' in name: - domain_name, name = name.split(':', 1) - if domain_name in self.domains: - domain = self.domains[domain_name] - element = getattr(domain, type)(name) - if element is not None: - return element, [] - # else look in the default domain - else: - def_domain = self.temp_data.get('default_domain') - if def_domain is not None: - element = getattr(def_domain, type)(name) - if element is not None: - return element, [] - # always look in the std domain - element = getattr(self.domains['std'], type)(name) - if element is not None: - return element, [] - raise ElementLookupError - - def patch_lookup_functions(self): - """Monkey-patch directive and role dispatch, so that domain-specific - markup takes precedence. - """ - def directive(name, lang_module, document): - try: - return self.lookup_domain_element('directive', name) - except ElementLookupError: - return orig_directive_function(name, lang_module, document) - - def role(name, lang_module, lineno, reporter): - try: - return self.lookup_domain_element('role', name) - except ElementLookupError: - return orig_role_function(name, lang_module, lineno, reporter) - - directives.directive = directive - roles.role = role - def read_doc(self, docname, app=None): """Parse a file and add/update inventory entries for the doctree.""" @@ -692,40 +641,39 @@ class BuildEnvironment(object): self.config.trim_footnote_reference_space self.settings['gettext_compact'] = self.config.gettext_compact - self.patch_lookup_functions() - docutilsconf = path.join(self.srcdir, 'docutils.conf') # read docutils.conf from source dir, not from current dir OptionParser.standard_config_files[1] = docutilsconf if path.isfile(docutilsconf): self.note_dependency(docutilsconf) - if self.config.default_role: - role_fn, messages = roles.role(self.config.default_role, english, - 0, dummy_reporter) - if role_fn: - roles._roles[''] = role_fn - else: - self.warn(docname, 'default role %s not found' % - self.config.default_role) + with sphinx_domains(self): + if self.config.default_role: + role_fn, messages = roles.role(self.config.default_role, english, + 0, dummy_reporter) + if role_fn: + roles._roles[''] = role_fn + else: + self.warn(docname, 'default role %s not found' % + self.config.default_role) - codecs.register_error('sphinx', self.warn_and_replace) + codecs.register_error('sphinx', self.warn_and_replace) - # publish manually - reader = SphinxStandaloneReader(self.app, parsers=self.config.source_parsers) - pub = Publisher(reader=reader, - writer=SphinxDummyWriter(), - destination_class=NullOutput) - pub.set_components(None, 'restructuredtext', None) - pub.process_programmatic_settings(None, self.settings, None) - src_path = self.doc2path(docname) - source = SphinxFileInput(app, self, source=None, source_path=src_path, - encoding=self.config.source_encoding) - pub.source = source - pub.settings._source = src_path - pub.set_destination(None, None) - pub.publish() - doctree = pub.document + # publish manually + reader = SphinxStandaloneReader(self.app, parsers=self.config.source_parsers) + pub = Publisher(reader=reader, + writer=SphinxDummyWriter(), + destination_class=NullOutput) + pub.set_components(None, 'restructuredtext', None) + pub.process_programmatic_settings(None, self.settings, None) + src_path = self.doc2path(docname) + source = SphinxFileInput(app, self, source=None, source_path=src_path, + encoding=self.config.source_encoding) + pub.source = source + pub.settings._source = src_path + pub.set_destination(None, None) + pub.publish() + doctree = pub.document # post-processing self.process_dependencies(docname, doctree) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index f39f62a9f..be9e2edad 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -26,3 +26,74 @@ def docutils_namespace(): finally: directives._directives = _directives roles._roles = _roles + + +class ElementLookupError(Exception): + pass + + +class sphinx_domains(object): + """Monkey-patch directive and role dispatch, so that domain-specific + markup takes precedence. + """ + def __init__(self, env): + self.env = env + self.directive_func = None + self.roles_func = None + + def __enter__(self): + self.enable() + + def __exit__(self, type, value, traceback): + self.disable() + + def enable(self): + self.directive_func = directives.directive + self.role_func = roles.role + + directives.directive = self.lookup_directive + roles.role = self.lookup_role + + def disable(self): + directives.directive = self.directive_func + roles.role = self.role_func + + def lookup_domain_element(self, type, name): + """Lookup a markup element (directive or role), given its name which can + be a full name (with domain). + """ + name = name.lower() + # explicit domain given? + if ':' in name: + domain_name, name = name.split(':', 1) + if domain_name in self.env.domains: + domain = self.env.domains[domain_name] + element = getattr(domain, type)(name) + if element is not None: + return element, [] + # else look in the default domain + else: + def_domain = self.env.temp_data.get('default_domain') + if def_domain is not None: + element = getattr(def_domain, type)(name) + if element is not None: + return element, [] + + # always look in the std domain + element = getattr(self.env.domains['std'], type)(name) + if element is not None: + return element, [] + + raise ElementLookupError + + def lookup_directive(self, name, lang_module, document): + try: + return self.lookup_domain_element('directive', name) + except ElementLookupError: + return self.directive_func(name, lang_module, document) + + def lookup_role(self, name, lang_module, lineno, reporter): + try: + return self.lookup_domain_element('role', name) + except ElementLookupError: + return self.role_func(name, lang_module, lineno, reporter) diff --git a/tests/test_markup.py b/tests/test_markup.py index 133f187d3..ec203447f 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -17,30 +17,33 @@ from docutils.parsers import rst from sphinx import addnodes from sphinx.util import texescape +from sphinx.util.docutils import sphinx_domains from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator from util import TestApp, with_app, assert_node -app = settings = parser = None +app = settings = parser = domain_context = None def setup_module(): - global app, settings, parser + global app, settings, parser, domain_context texescape.init() # otherwise done by the latex builder app = TestApp() optparser = frontend.OptionParser( components=(rst.Parser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() settings.env = app.builder.env - settings.env.patch_lookup_functions() settings.env.temp_data['docname'] = 'dummy' parser = rst.Parser() + domain_context = sphinx_domains(settings.env) + domain_context.enable() def teardown_module(): app.cleanup() + domain_context.disable() # since we're not resolving the markup afterwards, these nodes may remain