From 65e926cc57717b9d11fcf4dee9b8985adc715eb3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:04:19 +0100 Subject: [PATCH 01/11] #536: Include line number when complaining about missing reference targets in nitpicky mode. --- CHANGES | 3 +++ sphinx/environment.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 15b6e31bd..6fdae5466 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.7 (in development) ============================== +* #536: Include line number when complaining about missing reference + targets in nitpicky mode. + * #590: Fix inline display of graphviz diagrams in LaTeX output. * #589: Build using app.build() in setup command. diff --git a/sphinx/environment.py b/sphinx/environment.py index 2c0dc99ea..61e017f25 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1364,7 +1364,7 @@ class BuildEnvironment: 'reference target not found: %stype %s, target %s' % (node.get('refdomain') and 'domain %s, ' % node['refdomain'] or '', - typ, target)) + typ, target), node.line) except NoUri: newnode = contnode node.replace_self(newnode or contnode) From fad48a6a0fe9c25fd30eb630cb684d4d6ac9a497 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:06:11 +0100 Subject: [PATCH 02/11] Complain about unparseable C++ references. --- sphinx/domains/cpp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index c7074fb83..f07037e45 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1066,13 +1066,15 @@ class CPPDomain(Domain): contnode, name) parser = DefinitionParser(target) - # XXX: warn? try: expr = parser.parse_type().get_name() parser.skip_ws() if not parser.eof or expr is None: - return None + raise DefinitionError('') except DefinitionError: + refdoc = node.get('refdoc', fromdocname) + env.warn(refdoc, 'unparseable C++ definition: %r' % target, + node.line) return None parent = node['cpp:parent'] From 7139a1c882df653662570f88690bd52040974c73 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:09:49 +0100 Subject: [PATCH 03/11] The :rst:dir:`py:module` directive doesn't output its ``platform`` option value anymore. (It was the only thing that the directive did output, and therefore quite inconsistent.) --- CHANGES | 4 ++++ sphinx/domains/python.py | 11 ++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index e9db668e0..1389934c5 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,10 @@ Release 1.1 (in development) * Added a Texinfo builder. +* Incompatibility: The :rst:dir:`py:module` directive doesn't output + its ``platform`` option value anymore. (It was the only thing that + the directive did output, and therefore quite inconsistent.) + * Added i18n support for content, a ``gettext`` builder and related utilities. diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 0dbd883c7..2387f1e0f 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -419,15 +419,8 @@ class PyModule(Directive): targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] - # XXX this behavior of the module directive is a mess... - if 'platform' in self.options: - platform = self.options['platform'] - node = nodes.paragraph() - node += nodes.emphasis('', _('Platforms: ')) - node += nodes.Text(platform, platform) - ret.append(node) - # the synopsis isn't printed; in fact, it is only used in the - # modindex currently + # the platform and synopsis aren't printed; in fact, they are only used + # in the modindex currently if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[('single', indextext, From 388079984d8bd5c44a4ec2c8ce24d8e8bc013ea3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:41:15 +0100 Subject: [PATCH 04/11] #572: Show warnings by default when reference labels cannot be found. --- CHANGES | 3 +++ sphinx/domains/__init__.py | 2 ++ sphinx/domains/std.py | 45 +++++++++++++++----------------------- sphinx/environment.py | 20 +++++++++++------ sphinx/roles.py | 4 +++- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 6fdae5466..bc74696f8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.7 (in development) ============================== +* #572: Show warnings by default when reference labels cannot be + found. + * #536: Include line number when complaining about missing reference targets in nitpicky mode. diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index b16e61095..1d04a5933 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -132,6 +132,8 @@ class Domain(object): roles = {} #: a list of Index subclasses indices = [] + #: role name -> a warning message if reference is missing + dangling_warnings = {} #: data value for a fresh environment initial_data = {} diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 0625a4516..1fd015ac6 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -347,11 +347,13 @@ class StandardDomain(Domain): # links to tokens in grammar productions 'token': XRefRole(), # links to terms in glossary - 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis, + warn_dangling=True), # links to headings or arbitrary labels - 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis, + warn_dangling=True), # links to labels, without a different title - 'keyword': XRefRole(), + 'keyword': XRefRole(warn_dangling=True), } initial_data = { @@ -369,6 +371,13 @@ class StandardDomain(Domain): }, } + dangling_warnings = { + 'term': 'term not in glossary: %(target)s', + 'ref': 'undefined label: %(target)s (if the link has no caption ' + 'the label must precede a section header)', + 'keyword': 'unknown keyword: %(target)s', + } + def clear_doc(self, docname): for key, (fn, _) in self.data['progoptions'].items(): if fn == docname: @@ -426,27 +435,16 @@ class StandardDomain(Domain): def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if typ == 'ref': - #refdoc = node.get('refdoc', fromdocname) if node['refexplicit']: # reference to anonymous label; the reference uses # the supplied link caption docname, labelid = self.data['anonlabels'].get(target, ('','')) sectname = node.astext() - # XXX warn somehow if not resolved by intersphinx - #if not docname: - # env.warn(refdoc, 'undefined label: %s' % - # target, node.line) else: # reference to named label; the final node will # contain the section name after the label docname, labelid, sectname = self.data['labels'].get(target, ('','','')) - # XXX warn somehow if not resolved by intersphinx - #if not docname: - # env.warn(refdoc, - # 'undefined label: %s' % target + ' -- if you ' - # 'don\'t give a link caption the label must ' - # 'precede a section header.', node.line) if not docname: return None newnode = nodes.reference('', '', internal=True) @@ -470,20 +468,17 @@ class StandardDomain(Domain): # keywords are oddballs: they are referenced by named labels docname, labelid, _ = self.data['labels'].get(target, ('','','')) if not docname: - #env.warn(refdoc, 'unknown keyword: %s' % target) return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) elif typ == 'option': progname = node['refprogram'] docname, labelid = self.data['progoptions'].get((progname, target), ('', '')) if not docname: return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) else: objtypes = self.objtypes_for_role(typ) or [] for objtype in objtypes: @@ -493,13 +488,9 @@ class StandardDomain(Domain): else: docname, labelid = '', '' if not docname: - if typ == 'term': - env.warn(node.get('refdoc', fromdocname), - 'term not in glossary: %s' % target, node.line) return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) def get_objects(self): for (prog, option), info in self.data['progoptions'].iteritems(): diff --git a/sphinx/environment.py b/sphinx/environment.py index 61e017f25..65f98e66e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1315,9 +1315,10 @@ class BuildEnvironment: 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']] @@ -1359,12 +1360,17 @@ 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), node.line) + if newnode is None and not warned and \ + (self.config.nitpicky or node.get('refwarn')): + if domain and typ in domain.dangling_warnings: + msg = domain.dangling_warnings[typ] + elif node.get('refdomain') != '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) except NoUri: newnode = contnode node.replace_self(newnode or contnode) diff --git a/sphinx/roles.py b/sphinx/roles.py index fd3da416f..32d02da14 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -69,9 +69,10 @@ class XRefRole(object): innernodeclass = nodes.literal def __init__(self, fix_parens=False, lowercase=False, - nodeclass=None, innernodeclass=None): + nodeclass=None, innernodeclass=None, warn_dangling=False): self.fix_parens = fix_parens self.lowercase = lowercase + self.warn_dangling = warn_dangling if nodeclass is not None: self.nodeclass = nodeclass if innernodeclass is not None: @@ -133,6 +134,7 @@ class XRefRole(object): refnode += self.innernodeclass(rawtext, title, classes=classes) # we also need the source document refnode['refdoc'] = env.docname + refnode['refwarn'] = self.warn_dangling # result_nodes allow further modification of return values return self.result_nodes(inliner.document, env, refnode, is_ref=True) From c266128c6d0f06f4f11c34ddc6e533fd2c425ccc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:32:32 +0100 Subject: [PATCH 05/11] Rename "intl" module to "gettext", to make it easier to find. Distinguish environments with different versioning methods and always give the gettext builder its own doctree dir. --- doc/Makefile | 7 +-- doc/builders.rst | 2 +- doc/intl.rst | 2 +- sphinx/builders/__init__.py | 5 ++- sphinx/builders/{intl.py => gettext.py} | 5 ++- sphinx/builders/websupport.py | 1 + sphinx/environment.py | 60 ++++++++++++++++++------- sphinx/quickstart.py | 9 ++-- 8 files changed, 63 insertions(+), 28 deletions(-) rename sphinx/builders/{intl.py => gettext.py} (97%) diff --git a/doc/Makefile b/doc/Makefile index 0199ba47c..47951316a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,8 +8,9 @@ PAPER = PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ - $(SPHINXOPTS) $(O) . +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ + $(SPHINXOPTS) $(O) . +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) . .PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \ qthelp devhelp epub latex latexpdf changes linkcheck doctest @@ -116,7 +117,7 @@ latexpdf: @echo "pdflatex finished; the PDF files are in _build/latex." gettext: - $(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) _build/locale + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) _build/locale @echo @echo "Build finished. The message catalogs are in _build/locale." diff --git a/doc/builders.rst b/doc/builders.rst index 4a95e120e..b44245e97 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -240,7 +240,7 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf .. versionadded:: 0.5 -.. module:: sphinx.builders.intl +.. module:: sphinx.builders.gettext .. class:: MessageCatalogBuilder This builder produces gettext-style message catalos. Each top-level file or diff --git a/doc/intl.rst b/doc/intl.rst index 3a9e32f29..6a5471c4f 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -32,7 +32,7 @@ task to split up paragraphs which are too large as there is no sane automated way to do that. After Sphinx successfully ran the -:class:`~sphinx.builders.intl.MessageCatalogBuilder` you will find a collection +:class:`~sphinx.builders.gettext.MessageCatalogBuilder` you will find a collection of ``.pot`` files in your output directory. These are **catalog templates** and contain messages in your original language *only*. diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 339540335..5240a1c73 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -31,9 +31,12 @@ class Builder(object): name = '' # builder's output format, or '' if no document output is produced format = '' + # doctree versioning method + versioning_method = 'none' def __init__(self, app): self.env = app.env + self.env.set_versioning_method(self.versioning_method) self.srcdir = app.srcdir self.confdir = app.confdir self.outdir = app.outdir @@ -330,5 +333,5 @@ BUILTIN_BUILDERS = { 'changes': ('changes', 'ChangesBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), 'websupport': ('websupport', 'WebSupportBuilder'), - 'gettext': ('intl', 'MessageCatalogBuilder'), + 'gettext': ('gettext', 'MessageCatalogBuilder'), } diff --git a/sphinx/builders/intl.py b/sphinx/builders/gettext.py similarity index 97% rename from sphinx/builders/intl.py rename to sphinx/builders/gettext.py index 74ba03b54..1ff92360f 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/gettext.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.builders.intl - ~~~~~~~~~~~~~~~~~~~~ + sphinx.builders.gettext + ~~~~~~~~~~~~~~~~~~~~~~~ The MessageCatalogBuilder class. @@ -48,6 +48,7 @@ class I18nBuilder(Builder): General i18n builder. """ name = 'i18n' + versioning_method = 'text' def init(self): Builder.init(self) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index e8f6aef35..b77573095 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -26,6 +26,7 @@ class WebSupportBuilder(PickleHTMLBuilder): Builds documents for the web support package. """ name = 'websupport' + versioning_method = 'commentable' def init(self): PickleHTMLBuilder.init(self) diff --git a/sphinx/environment.py b/sphinx/environment.py index 32cc44a8b..75292299c 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 @@ -79,6 +80,12 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) +versioning_methods = { + 'none': False, + 'text': nodes.TextElement, + 'commentable': is_commentable, +} + class WarningStream(object): def __init__(self, warnfunc): @@ -313,6 +320,9 @@ class BuildEnvironment: self.srcdir = srcdir self.config = config + # the method of doctree versioning; see set_versioning_method + self.versioning_method = None + # the application object; only set while update() runs self.app = None @@ -380,6 +390,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_methods: + raise ValueError('invalid versioning method: %r' % method) + method = versioning_methods[method] + if self.versioning_method not in (None, method): + raise SphinxError('This environment is incompatible with the ' + 'selected builder, please choose another ' + 'doctree directory.') + self.versioning_method = method + def warn(self, docname, msg, lineno=None): # strange argument order is due to backwards compatibility self._warnfunc(msg, (docname, lineno)) @@ -754,25 +781,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_method: + # 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 + 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, nodes.TextElement)) - else: - list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + # add uids for versioning + if old_doctree is None: + list(add_uids(doctree, nodes.TextElement)) + else: + list(merge_doctrees( + old_doctree, doctree, self.versioning_method)) # make it picklable doctree.reporter = None diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 4d7e2db3f..0818ad0a3 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -361,6 +361,8 @@ PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) %(rsrcdir)s .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \ epub latex latexpdf text man changes linkcheck doctest gettext @@ -483,7 +485,7 @@ info: \t@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: -\t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale +\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale \t@echo \t@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." @@ -514,8 +516,10 @@ if "%%SPHINXBUILD%%" == "" ( ) set BUILDDIR=%(rbuilddir)s set ALLSPHINXOPTS=-d %%BUILDDIR%%/doctrees %%SPHINXOPTS%% %(rsrcdir)s +set I18NSPHINXOPTS=%%SPHINXOPTS%% %(rsrcdir)s if NOT "%%PAPER%%" == "" ( \tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%% +\tset I18NSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%I18NSPHINXOPTS%% ) if "%%1" == "" goto help @@ -659,7 +663,7 @@ if "%%1" == "texinfo" ( ) if "%%1" == "gettext" ( -\t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale +\t%%SPHINXBUILD%% -b gettext %%I18NSPHINXOPTS%% %%BUILDDIR%%/locale \tif errorlevel 1 exit /b 1 \techo. \techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale. @@ -991,4 +995,3 @@ def main(argv=sys.argv): print print '[Interrupted.]' return - From 661101663ae1d3a9838fd28b2811e03ff113b31b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:33:02 +0100 Subject: [PATCH 06/11] Bump env version after addition of versioning_method. --- sphinx/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 75292299c..a87db0e07 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -69,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([ From 2524f31069838a9bfa71b728258f2add3e8b3297 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:36:00 +0100 Subject: [PATCH 07/11] Rename attribute to better fit the purpose. --- sphinx/environment.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index a87db0e07..1ffbac64e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -80,7 +80,7 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) -versioning_methods = { +versioning_conditions = { 'none': False, 'text': nodes.TextElement, 'commentable': is_commentable, @@ -321,7 +321,7 @@ class BuildEnvironment: self.config = config # the method of doctree versioning; see set_versioning_method - self.versioning_method = None + self.versioning_condition = None # the application object; only set while update() runs self.app = None @@ -398,14 +398,14 @@ class BuildEnvironment: raise an exception if the user tries to use an environment with an incompatible versioning method. """ - if method not in versioning_methods: + if method not in versioning_conditions: raise ValueError('invalid versioning method: %r' % method) - method = versioning_methods[method] - if self.versioning_method not in (None, 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_method = method + self.versioning_condition = condition def warn(self, docname, msg, lineno=None): # strange argument order is due to backwards compatibility @@ -781,7 +781,7 @@ class BuildEnvironment: # store time of build, for outdated files detection self.all_docs[docname] = time.time() - if self.versioning_method: + if self.versioning_condition: # get old doctree try: f = open(self.doc2path(docname, @@ -795,10 +795,10 @@ class BuildEnvironment: # add uids for versioning if old_doctree is None: - list(add_uids(doctree, nodes.TextElement)) + list(add_uids(doctree, self.versioning_condition)) else: list(merge_doctrees( - old_doctree, doctree, self.versioning_method)) + old_doctree, doctree, self.versioning_condition)) # make it picklable doctree.reporter = None From a20b68cd3e416d72f236cc1abd797c51e3a1ef08 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:55:34 +0100 Subject: [PATCH 08/11] Refactor warning-emission for unknown references. --- sphinx/environment.py | 57 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 1ffbac64e..43e0262bf 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1411,7 +1411,6 @@ class BuildEnvironment: typ = node['reftype'] target = node['reftarget'] refdoc = node.get('refdoc', fromdocname) - warned = False domain = None try: @@ -1428,11 +1427,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() @@ -1445,11 +1440,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 @@ -1457,21 +1448,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 or node.get('refwarn')): - if domain and typ in domain.dangling_warnings: - msg = domain.dangling_warnings[typ] - elif node.get('refdomain') != '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) + 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']) @@ -1487,9 +1497,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 From 8814e973897d6bdaa13279997e016b95111ee8be Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:59:44 +0100 Subject: [PATCH 09/11] Always warn on missing :doc: or citation references. --- sphinx/environment.py | 3 ++- sphinx/roles.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 43e0262bf..1f8e00f56 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -189,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) diff --git a/sphinx/roles.py b/sphinx/roles.py index 1511e35f9..307439146 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -300,7 +300,7 @@ specific_docroles = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), # links to documents - 'doc': XRefRole(), + 'doc': XRefRole(warn_dangling=True), 'pep': indexmarkup_role, 'rfc': indexmarkup_role, From a2934e33b8f051569e7d4f421502e6a184742e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 Jan 2011 18:03:17 +0100 Subject: [PATCH 10/11] Replace unneeded defaultdict with dict --- sphinx/versioning.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index f50f80b0d..74355efed 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -11,7 +11,6 @@ """ from uuid import uuid4 from operator import itemgetter -from collections import defaultdict from sphinx.util.pycompat import product, zip_longest @@ -49,7 +48,7 @@ def merge_doctrees(old, new, condition): new_iter = new.traverse(condition) old_nodes = [] new_nodes = [] - ratios = defaultdict(list) + ratios = {} seen = set() # compare the nodes each doctree in order for old_node, new_node in zip_longest(old_iter, new_iter): From 1961a4aaa83914d317d32efb23cc3d6ca297377a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 18:06:38 +0100 Subject: [PATCH 11/11] Fix typo in name. --- doc/theming.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/theming.rst b/doc/theming.rst index 216118c1b..4892299f0 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -179,7 +179,7 @@ These themes are: *nosidebar*. * **pyramid** -- A theme from the Pyramid web framework project, designed by - Blais Laflamme. THere are currently no options beyond *nosidebar*. + Blaise Laflamme. THere are currently no options beyond *nosidebar*. * **haiku** -- A theme without sidebar inspired by the `Haiku OS user guide `_. The following