diff options
Diffstat (limited to 'sphinx/environment.py')
| -rw-r--r-- | sphinx/environment.py | 122 |
1 files changed, 81 insertions, 41 deletions
diff --git a/sphinx/environment.py b/sphinx/environment.py index 59debcd8..1f8e00f5 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -43,6 +43,7 @@ from sphinx.util.nodes import clean_astext, make_refnode, extract_messages from sphinx.util.osutil import movefile, SEP, ustrftime from sphinx.util.matching import compile_matchers from sphinx.util.pycompat import all, class_types +from sphinx.util.websupport import is_commentable from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _, init as init_locale from sphinx.versioning import add_uids, merge_doctrees @@ -68,7 +69,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 39 +ENV_VERSION = 40 default_substitutions = set([ @@ -79,6 +80,12 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) +versioning_conditions = { + 'none': False, + 'text': nodes.TextElement, + 'commentable': is_commentable, +} + class WarningStream(object): def __init__(self, warnfunc): @@ -182,7 +189,8 @@ class CitationReferences(Transform): for citnode in self.document.traverse(nodes.citation_reference): cittext = citnode.astext() refnode = addnodes.pending_xref(cittext, reftype='citation', - reftarget=cittext) + reftarget=cittext, refwarn=True) + refnode.line = citnode.line or citnode.parent.line refnode += nodes.Text('[' + cittext + ']') citnode.parent.replace(citnode, refnode) @@ -313,6 +321,9 @@ class BuildEnvironment: self.srcdir = srcdir self.config = config + # the method of doctree versioning; see set_versioning_method + self.versioning_condition = None + # the application object; only set while update() runs self.app = None @@ -380,6 +391,23 @@ class BuildEnvironment: self._warnfunc = func self.settings['warning_stream'] = WarningStream(func) + def set_versioning_method(self, method): + """This sets the doctree versioning method for this environment. + + Versioning methods are a builder property; only builders with the same + versioning method can share the same doctree directory. Therefore, we + raise an exception if the user tries to use an environment with an + incompatible versioning method. + """ + if method not in versioning_conditions: + raise ValueError('invalid versioning method: %r' % method) + condition = versioning_conditions[method] + if self.versioning_condition not in (None, condition): + raise SphinxError('This environment is incompatible with the ' + 'selected builder, please choose another ' + 'doctree directory.') + self.versioning_condition = condition + def warn(self, docname, msg, lineno=None): # strange argument order is due to backwards compatibility self._warnfunc(msg, (docname, lineno)) @@ -754,25 +782,24 @@ class BuildEnvironment: # store time of build, for outdated files detection self.all_docs[docname] = time.time() - # get old doctree - old_doctree_path = self.doc2path(docname, self.doctreedir, '.doctree') - try: - f = open(old_doctree_path, 'rb') + if self.versioning_condition: + # get old doctree try: - old_doctree = pickle.load(f) - finally: - f.close() - old_doctree.settings.env = self - old_doctree.reporter = Reporter(self.doc2path(docname), 2, 5, - stream=WarningStream(self._warnfunc)) - except EnvironmentError: - old_doctree = None - - # add uids for versioning - if old_doctree is None: - list(add_uids(doctree, nodes.TextElement)) - else: - list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + f = open(self.doc2path(docname, + self.doctreedir, '.doctree'), 'rb') + try: + old_doctree = pickle.load(f) + finally: + f.close() + except EnvironmentError: + old_doctree = None + + # add uids for versioning + if old_doctree is None: + list(add_uids(doctree, self.versioning_condition)) + else: + list(merge_doctrees( + old_doctree, doctree, self.versioning_condition)) # make it picklable doctree.reporter = None @@ -1385,10 +1412,10 @@ class BuildEnvironment: typ = node['reftype'] target = node['reftarget'] refdoc = node.get('refdoc', fromdocname) - warned = False + domain = None try: - if node.has_key('refdomain') and node['refdomain']: + if 'refdomain' in node and node['refdomain']: # let the domain try to resolve the reference try: domain = self.domains[node['refdomain']] @@ -1401,11 +1428,7 @@ class BuildEnvironment: # directly reference to document by source name; # can be absolute or relative docname = docname_join(refdoc, target) - if docname not in self.all_docs: - self.warn(refdoc, - 'unknown document: %s' % docname, node.line) - warned = True - else: + if docname in self.all_docs: if node['refexplicit']: # reference with explicit title caption = node.astext() @@ -1418,11 +1441,7 @@ class BuildEnvironment: newnode.append(innernode) elif typ == 'citation': docname, labelid = self.citations.get(target, ('', '')) - if not docname: - self.warn(refdoc, - 'citation not found: %s' % target, node.line) - warned = True - else: + if docname: newnode = make_refnode(builder, fromdocname, docname, labelid, contnode) # no new node found? try the missing-reference event @@ -1430,16 +1449,40 @@ class BuildEnvironment: newnode = builder.app.emit_firstresult( 'missing-reference', self, node, contnode) # still not found? warn if in nit-picky mode - if newnode is None and not warned and self.config.nitpicky: - self.warn(refdoc, - 'reference target not found: %stype %s, target %s' - % (node.get('refdomain') and - 'domain %s, ' % node['refdomain'] or '', - typ, target)) + if newnode is None: + self._warn_missing_reference( + fromdocname, typ, target, node, domain) except NoUri: newnode = contnode node.replace_self(newnode or contnode) + # remove only-nodes that do not belong to our builder + self.process_only_nodes(doctree, fromdocname, builder) + + # allow custom references to be resolved + builder.app.emit('doctree-resolved', doctree, fromdocname) + + def _warn_missing_reference(self, fromdoc, typ, target, node, domain): + warn = node.get('refwarn') + if self.config.nitpicky: + warn = True # XXX process exceptions here + if not warn: + return + refdoc = node.get('refdoc', fromdoc) + if domain and typ in domain.dangling_warnings: + msg = domain.dangling_warnings[typ] + elif typ == 'doc': + msg = 'unknown document: %(target)s' + elif typ == 'citation': + msg = 'citation not found: %(target)s' + elif node.get('refdomain', 'std') != 'std': + msg = '%s:%s reference target not found: %%(target)s' % \ + (node['refdomain'], typ) + else: + msg = '%s reference target not found: %%(target)s' % typ + self.warn(refdoc, msg % {'target': target}, node.line) + + def process_only_nodes(self, doctree, fromdocname, builder): for node in doctree.traverse(addnodes.only): try: ret = builder.tags.eval_condition(node['expr']) @@ -1455,9 +1498,6 @@ class BuildEnvironment: # if there is a target node before the only node node.replace_self(nodes.comment()) - # allow custom references to be resolved - builder.app.emit('doctree-resolved', doctree, fromdocname) - def assign_section_numbers(self): """Assign a section number to each heading under a numbered toctree.""" # a list of all docnames whose section numbers changed |
