Refactor directive/role lookup from domains a bit to avoid code duplication.

This commit is contained in:
Georg Brandl 2009-09-12 10:14:27 +00:00
parent 52d5411f6d
commit 68c545cf22
3 changed files with 63 additions and 62 deletions

View File

@ -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))

View File

@ -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

View File

@ -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)