From 68c545cf22c783b67a501c0ba07fd1edb91bef5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 12 Sep 2009 10:14:27 +0000 Subject: [PATCH] Refactor directive/role lookup from domains a bit to avoid code duplication. --- sphinx/directives/__init__.py | 2 +- sphinx/environment.py | 120 +++++++++++++++++----------------- tests/test_markup.py | 3 +- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 3867ab42d..6183c31dc 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -284,7 +284,7 @@ class DefaultDomain(Directive): def run(self): env = self.state.document.settings.env domain_name = arguments[0] - env.default_domain = env.domains.get(domain_name) + env.doc_read_data['default_domain'] = env.domains.get(domain_name) directives.register_directive('default-domain', directive_dwim(DefaultDomain)) diff --git a/sphinx/environment.py b/sphinx/environment.py index c8cb27a3b..ce39760cc 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -40,9 +40,39 @@ from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \ docname_join, FilenameUniqDict, url_re, make_refnode from sphinx.errors import SphinxError, ExtensionError + orig_role_function = roles.role orig_directive_function = directives.directive +class ElementLookupError(Exception): pass + +def lookup_domain_element(env, 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 env.domains: + domain = 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 = env.doc_read_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(env.domains['std'], type)(name) + if element is not None: + return element, [] + raise ElementLookupError + + default_settings = { 'embed_stylesheet': False, 'cloak_email_addresses': True, @@ -538,6 +568,25 @@ class BuildEnvironment: error.object[error.end:lineend]), lineno) return (u'?', error.end) + 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 lookup_domain_element(self, 'directive', name) + except ElementLookupError: + return orig_directive_function(name, lang_module, document) + + def role(name, lang_module, lineno, reporter): + try: + return lookup_domain_element(self, 'role', name) + except ElementLookupError: + return orig_role_function(name, lang_module, lineno, reporter) + + directives.directive = directive + roles.role = role + def read_doc(self, docname, src_path=None, save_parsed=True, app=None): """ Parse a file and add/update inventory entries for the doctree. @@ -562,11 +611,15 @@ class BuildEnvironment: self.config.default_role) self.doc_read_data['docname'] = docname + # defaults to the global default, but can be re-set in a document + self.doc_read_data['default_domain'] = \ + self.domains.get(self.config.default_domain) + self.settings['input_encoding'] = self.config.source_encoding self.settings['trim_footnote_reference_space'] = \ self.config.trim_footnote_reference_space - codecs.register_error('sphinx', self.warn_and_replace) + self.patch_lookup_functions() codecs.register_error('sphinx', self.warn_and_replace) @@ -585,61 +638,6 @@ class BuildEnvironment: else: return data - # defaults to the global default, but can be re-set in a document - self.default_domain = self.domains.get(self.config.default_domain) - - # monkey-patch, so that domain directives take precedence - def directive(directive_name, language_module, document): - """Lookup a directive.""" - directive_name = directive_name.lower() - # explicit domain given? - if ':' in directive_name: - domain_name, directive_name = directive_name.split(':', 1) - if domain_name in self.domains: - domain = self.domains[domain_name] - directive = domain.directive(directive_name) - if directive is not None: - return directive, [] - # else look in the default domain - elif self.default_domain is not None: - directive = self.default_domain.directive(directive_name) - if directive is not None: - return directive, [] - # always look in the std domain - # (XXX or register them in the docutils namespace?) - directive = self.domains['std'].directive(directive_name) - if directive is not None: - return directive, [] - # last, look in the default docutils namespace - return orig_directive_function(directive_name, language_module, - document) - directives.directive = directive - - def role(role_name, language_module, lineno, reporter): - """Lookup a role name.""" - role_name = role_name.lower() - # explicit domain given? - if ':' in role_name: - domain_name, role_name = role_name.split(':', 1) - if domain_name in self.domains: - domain = self.domains[domain_name] - role = domain.role(role_name) - if role is not None: - return role, [] - # else look in the default domain - elif self.default_domain is not None: - role = self.default_domain.role(role_name) - if role is not None: - return role, [] - # always look in the std domain - role = self.domains['std'].role(role_name) - if role is not None: - return role, [] - # last, look in the default docutils namespace - return orig_role_function(role_name, language_module, - lineno, reporter) - roles.role = role - # publish manually pub = Publisher(reader=SphinxStandaloneReader(), writer=SphinxDummyWriter(), @@ -654,6 +652,8 @@ class BuildEnvironment: doctree = pub.document except UnicodeError, err: raise SphinxError(str(err)) + + # post-processing self.filter_messages(doctree) self.process_dependencies(docname, doctree) self.process_images(docname, doctree) @@ -665,12 +665,13 @@ class BuildEnvironment: self.note_citations_from(docname, doctree) self.build_toc_from(docname, doctree) - # store time of reading, used to find outdated files - self.all_docs[docname] = time.time() - + # allow extension-specific post-processing if app: app.emit('doctree-read', doctree) + # store time of reading, used to find outdated files + self.all_docs[docname] = time.time() + # make it picklable doctree.reporter = None doctree.transformer = None @@ -683,7 +684,6 @@ class BuildEnvironment: # cleanup self.doc_read_data.clear() - self.default_domain = None if save_parsed: # save the parsed doctree diff --git a/tests/test_markup.py b/tests/test_markup.py index 03c421a13..22481953a 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -29,6 +29,7 @@ def setup_module(): components=(rst.Parser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() settings.env = app.builder.env + settings.env.patch_lookup_functions() parser = rst.Parser() def teardown_module(): @@ -60,7 +61,7 @@ def verify_re(rst, html_expected, latex_expected): html_translator = ForgivingHTMLTranslator(app.builder, document) document.walkabout(html_translator) html_translated = ''.join(html_translator.fragment).strip() - assert re.match(html_expected, html_translated), 'from' + rst + assert re.match(html_expected, html_translated), 'from ' + rst if latex_expected: latex_translator = ForgivingLaTeXTranslator(document, app.builder)