getargspec moved to sphinx.util.inspect

This commit is contained in:
Łukasz Langa 2011-01-08 18:33:29 +01:00
commit 0964f4f993
18 changed files with 178 additions and 125 deletions

10
CHANGES
View File

@ -5,6 +5,10 @@ Release 1.1 (in development)
* Added a Texinfo builder. * 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 * Added i18n support for content, a ``gettext`` builder and
related utilities. related utilities.
@ -85,6 +89,12 @@ Release 1.1 (in development)
Release 1.0.7 (in development) 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.
* #590: Fix inline display of graphviz diagrams in LaTeX output. * #590: Fix inline display of graphviz diagrams in LaTeX output.
* #589: Build using app.build() in setup command. * #589: Build using app.build() in setup command.

View File

@ -8,8 +8,9 @@ PAPER =
PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \
$(SPHINXOPTS) $(O) . $(SPHINXOPTS) $(O) .
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) .
.PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \ .PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \
qthelp devhelp epub latex latexpdf changes linkcheck doctest qthelp devhelp epub latex latexpdf changes linkcheck doctest
@ -116,7 +117,7 @@ latexpdf:
@echo "pdflatex finished; the PDF files are in _build/latex." @echo "pdflatex finished; the PDF files are in _build/latex."
gettext: gettext:
$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) _build/locale $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) _build/locale
@echo @echo
@echo "Build finished. The message catalogs are in _build/locale." @echo "Build finished. The message catalogs are in _build/locale."

View File

@ -240,7 +240,7 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf
.. versionadded:: 0.5 .. versionadded:: 0.5
.. module:: sphinx.builders.intl .. module:: sphinx.builders.gettext
.. class:: MessageCatalogBuilder .. class:: MessageCatalogBuilder
This builder produces gettext-style message catalos. Each top-level file or This builder produces gettext-style message catalos. Each top-level file or

View File

@ -32,7 +32,7 @@ task to split up paragraphs which are too large as there is no sane automated
way to do that. way to do that.
After Sphinx successfully ran the 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** of ``.pot`` files in your output directory. These are **catalog templates**
and contain messages in your original language *only*. and contain messages in your original language *only*.

View File

@ -179,7 +179,7 @@ These themes are:
*nosidebar*. *nosidebar*.
* **pyramid** -- A theme from the Pyramid web framework project, designed by * **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 * **haiku** -- A theme without sidebar inspired by the `Haiku OS user guide
<http://www.haiku-os.org/docs/userguide/en/contents.html>`_. The following <http://www.haiku-os.org/docs/userguide/en/contents.html>`_. The following

View File

@ -31,9 +31,12 @@ class Builder(object):
name = '' name = ''
# builder's output format, or '' if no document output is produced # builder's output format, or '' if no document output is produced
format = '' format = ''
# doctree versioning method
versioning_method = 'none'
def __init__(self, app): def __init__(self, app):
self.env = app.env self.env = app.env
self.env.set_versioning_method(self.versioning_method)
self.srcdir = app.srcdir self.srcdir = app.srcdir
self.confdir = app.confdir self.confdir = app.confdir
self.outdir = app.outdir self.outdir = app.outdir
@ -330,5 +333,5 @@ BUILTIN_BUILDERS = {
'changes': ('changes', 'ChangesBuilder'), 'changes': ('changes', 'ChangesBuilder'),
'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'),
'websupport': ('websupport', 'WebSupportBuilder'), 'websupport': ('websupport', 'WebSupportBuilder'),
'gettext': ('intl', 'MessageCatalogBuilder'), 'gettext': ('gettext', 'MessageCatalogBuilder'),
} }

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sphinx.builders.intl sphinx.builders.gettext
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
The MessageCatalogBuilder class. The MessageCatalogBuilder class.
@ -48,6 +48,7 @@ class I18nBuilder(Builder):
General i18n builder. General i18n builder.
""" """
name = 'i18n' name = 'i18n'
versioning_method = 'text'
def init(self): def init(self):
Builder.init(self) Builder.init(self)

View File

@ -26,6 +26,7 @@ class WebSupportBuilder(PickleHTMLBuilder):
Builds documents for the web support package. Builds documents for the web support package.
""" """
name = 'websupport' name = 'websupport'
versioning_method = 'commentable'
def init(self): def init(self):
PickleHTMLBuilder.init(self) PickleHTMLBuilder.init(self)

View File

@ -131,6 +131,8 @@ class Domain(object):
roles = {} roles = {}
#: a list of Index subclasses #: a list of Index subclasses
indices = [] indices = []
#: role name -> a warning message if reference is missing
dangling_warnings = {}
#: data value for a fresh environment #: data value for a fresh environment
initial_data = {} initial_data = {}

View File

@ -1090,13 +1090,15 @@ class CPPDomain(Domain):
contnode, name) contnode, name)
parser = DefinitionParser(target) parser = DefinitionParser(target)
# XXX: warn?
try: try:
expr = parser.parse_type().get_name() expr = parser.parse_type().get_name()
parser.skip_ws() parser.skip_ws()
if not parser.eof or expr is None: if not parser.eof or expr is None:
return None raise DefinitionError('')
except DefinitionError: except DefinitionError:
refdoc = node.get('refdoc', fromdocname)
env.warn(refdoc, 'unparseable C++ definition: %r' % target,
node.line)
return None return None
parent = node['cpp:parent'] parent = node['cpp:parent']

View File

@ -419,15 +419,8 @@ class PyModule(Directive):
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
self.state.document.note_explicit_target(targetnode) self.state.document.note_explicit_target(targetnode)
ret = [targetnode] ret = [targetnode]
# XXX this behavior of the module directive is a mess... # the platform and synopsis aren't printed; in fact, they are only used
if 'platform' in self.options: # in the modindex currently
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
if not noindex: if not noindex:
indextext = _('%s (module)') % modname indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext, inode = addnodes.index(entries=[('single', indextext,

View File

@ -411,11 +411,13 @@ class StandardDomain(Domain):
# links to tokens in grammar productions # links to tokens in grammar productions
'token': XRefRole(), 'token': XRefRole(),
# links to terms in glossary # 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 # 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 # links to labels, without a different title
'keyword': XRefRole(), 'keyword': XRefRole(warn_dangling=True),
} }
initial_data = { initial_data = {
@ -433,6 +435,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): def clear_doc(self, docname):
for key, (fn, _) in self.data['progoptions'].items(): for key, (fn, _) in self.data['progoptions'].items():
if fn == docname: if fn == docname:
@ -490,27 +499,16 @@ class StandardDomain(Domain):
def resolve_xref(self, env, fromdocname, builder, def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode): typ, target, node, contnode):
if typ == 'ref': if typ == 'ref':
#refdoc = node.get('refdoc', fromdocname)
if node['refexplicit']: if node['refexplicit']:
# reference to anonymous label; the reference uses # reference to anonymous label; the reference uses
# the supplied link caption # the supplied link caption
docname, labelid = self.data['anonlabels'].get(target, ('','')) docname, labelid = self.data['anonlabels'].get(target, ('',''))
sectname = node.astext() sectname = node.astext()
# XXX warn somehow if not resolved by intersphinx
#if not docname:
# env.warn(refdoc, 'undefined label: %s' %
# target, node.line)
else: else:
# reference to named label; the final node will # reference to named label; the final node will
# contain the section name after the label # contain the section name after the label
docname, labelid, sectname = self.data['labels'].get(target, 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: if not docname:
return None return None
newnode = nodes.reference('', '', internal=True) newnode = nodes.reference('', '', internal=True)
@ -534,20 +532,17 @@ class StandardDomain(Domain):
# keywords are oddballs: they are referenced by named labels # keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.data['labels'].get(target, ('','','')) docname, labelid, _ = self.data['labels'].get(target, ('','',''))
if not docname: if not docname:
#env.warn(refdoc, 'unknown keyword: %s' % target)
return None return None
else: return make_refnode(builder, fromdocname, docname,
return make_refnode(builder, fromdocname, docname, labelid, contnode)
labelid, contnode)
elif typ == 'option': elif typ == 'option':
progname = node['refprogram'] progname = node['refprogram']
docname, labelid = self.data['progoptions'].get((progname, target), docname, labelid = self.data['progoptions'].get((progname, target),
('', '')) ('', ''))
if not docname: if not docname:
return None return None
else: return make_refnode(builder, fromdocname, docname,
return make_refnode(builder, fromdocname, docname, labelid, contnode)
labelid, contnode)
else: else:
objtypes = self.objtypes_for_role(typ) or [] objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes: for objtype in objtypes:
@ -557,13 +552,9 @@ class StandardDomain(Domain):
else: else:
docname, labelid = '', '' docname, labelid = '', ''
if not docname: if not docname:
if typ == 'term':
env.warn(node.get('refdoc', fromdocname),
'term not in glossary: %s' % target, node.line)
return None return None
else: return make_refnode(builder, fromdocname, docname,
return make_refnode(builder, fromdocname, docname, labelid, contnode)
labelid, contnode)
def get_objects(self): def get_objects(self):
for (prog, option), info in self.data['progoptions'].iteritems(): for (prog, option), info in self.data['progoptions'].iteritems():

View File

@ -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.osutil import movefile, SEP, ustrftime
from sphinx.util.matching import compile_matchers from sphinx.util.matching import compile_matchers
from sphinx.util.pycompat import all, class_types from sphinx.util.pycompat import all, class_types
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _, init as init_locale from sphinx.locale import _, init as init_locale
from sphinx.versioning import add_uids, merge_doctrees from sphinx.versioning import add_uids, merge_doctrees
@ -68,7 +69,7 @@ default_settings = {
# This is increased every time an environment attribute is added # This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files. # or changed to properly invalidate pickle files.
ENV_VERSION = 39 ENV_VERSION = 40
default_substitutions = set([ default_substitutions = set([
@ -79,6 +80,12 @@ default_substitutions = set([
dummy_reporter = Reporter('', 4, 4) dummy_reporter = Reporter('', 4, 4)
versioning_conditions = {
'none': False,
'text': nodes.TextElement,
'commentable': is_commentable,
}
class WarningStream(object): class WarningStream(object):
def __init__(self, warnfunc): def __init__(self, warnfunc):
@ -182,7 +189,8 @@ class CitationReferences(Transform):
for citnode in self.document.traverse(nodes.citation_reference): for citnode in self.document.traverse(nodes.citation_reference):
cittext = citnode.astext() cittext = citnode.astext()
refnode = addnodes.pending_xref(cittext, reftype='citation', 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 + ']') refnode += nodes.Text('[' + cittext + ']')
citnode.parent.replace(citnode, refnode) citnode.parent.replace(citnode, refnode)
@ -313,6 +321,9 @@ class BuildEnvironment:
self.srcdir = srcdir self.srcdir = srcdir
self.config = config self.config = config
# the method of doctree versioning; see set_versioning_method
self.versioning_condition = None
# the application object; only set while update() runs # the application object; only set while update() runs
self.app = None self.app = None
@ -380,6 +391,23 @@ class BuildEnvironment:
self._warnfunc = func self._warnfunc = func
self.settings['warning_stream'] = WarningStream(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): def warn(self, docname, msg, lineno=None):
# strange argument order is due to backwards compatibility # strange argument order is due to backwards compatibility
self._warnfunc(msg, (docname, lineno)) self._warnfunc(msg, (docname, lineno))
@ -754,25 +782,24 @@ class BuildEnvironment:
# store time of build, for outdated files detection # store time of build, for outdated files detection
self.all_docs[docname] = time.time() self.all_docs[docname] = time.time()
# get old doctree if self.versioning_condition:
old_doctree_path = self.doc2path(docname, self.doctreedir, '.doctree') # get old doctree
try:
f = open(old_doctree_path, 'rb')
try: try:
old_doctree = pickle.load(f) f = open(self.doc2path(docname,
finally: self.doctreedir, '.doctree'), 'rb')
f.close() try:
old_doctree.settings.env = self old_doctree = pickle.load(f)
old_doctree.reporter = Reporter(self.doc2path(docname), 2, 5, finally:
stream=WarningStream(self._warnfunc)) f.close()
except EnvironmentError: except EnvironmentError:
old_doctree = None old_doctree = None
# add uids for versioning # add uids for versioning
if old_doctree is None: if old_doctree is None:
list(add_uids(doctree, nodes.TextElement)) list(add_uids(doctree, self.versioning_condition))
else: else:
list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) list(merge_doctrees(
old_doctree, doctree, self.versioning_condition))
# make it picklable # make it picklable
doctree.reporter = None doctree.reporter = None
@ -1385,10 +1412,10 @@ class BuildEnvironment:
typ = node['reftype'] typ = node['reftype']
target = node['reftarget'] target = node['reftarget']
refdoc = node.get('refdoc', fromdocname) refdoc = node.get('refdoc', fromdocname)
warned = False domain = None
try: try:
if node.has_key('refdomain') and node['refdomain']: if 'refdomain' in node and node['refdomain']:
# let the domain try to resolve the reference # let the domain try to resolve the reference
try: try:
domain = self.domains[node['refdomain']] domain = self.domains[node['refdomain']]
@ -1401,11 +1428,7 @@ class BuildEnvironment:
# directly reference to document by source name; # directly reference to document by source name;
# can be absolute or relative # can be absolute or relative
docname = docname_join(refdoc, target) docname = docname_join(refdoc, target)
if docname not in self.all_docs: if docname in self.all_docs:
self.warn(refdoc,
'unknown document: %s' % docname, node.line)
warned = True
else:
if node['refexplicit']: if node['refexplicit']:
# reference with explicit title # reference with explicit title
caption = node.astext() caption = node.astext()
@ -1418,11 +1441,7 @@ class BuildEnvironment:
newnode.append(innernode) newnode.append(innernode)
elif typ == 'citation': elif typ == 'citation':
docname, labelid = self.citations.get(target, ('', '')) docname, labelid = self.citations.get(target, ('', ''))
if not docname: if docname:
self.warn(refdoc,
'citation not found: %s' % target, node.line)
warned = True
else:
newnode = make_refnode(builder, fromdocname, docname, newnode = make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
# no new node found? try the missing-reference event # no new node found? try the missing-reference event
@ -1430,16 +1449,40 @@ class BuildEnvironment:
newnode = builder.app.emit_firstresult( newnode = builder.app.emit_firstresult(
'missing-reference', self, node, contnode) 'missing-reference', self, node, contnode)
# still not found? warn if in nit-picky mode # still not found? warn if in nit-picky mode
if newnode is None and not warned and self.config.nitpicky: if newnode is None:
self.warn(refdoc, self._warn_missing_reference(
'reference target not found: %stype %s, target %s' fromdocname, typ, target, node, domain)
% (node.get('refdomain') and
'domain %s, ' % node['refdomain'] or '',
typ, target))
except NoUri: except NoUri:
newnode = contnode newnode = contnode
node.replace_self(newnode or 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): for node in doctree.traverse(addnodes.only):
try: try:
ret = builder.tags.eval_condition(node['expr']) ret = builder.tags.eval_condition(node['expr'])
@ -1455,9 +1498,6 @@ class BuildEnvironment:
# if there is a target node before the only node # if there is a target node before the only node
node.replace_self(nodes.comment()) node.replace_self(nodes.comment())
# allow custom references to be resolved
builder.app.emit('doctree-resolved', doctree, fromdocname)
def assign_section_numbers(self): def assign_section_numbers(self):
"""Assign a section number to each heading under a numbered toctree.""" """Assign a section number to each heading under a numbered toctree."""
# a list of all docnames whose section numbers changed # a list of all docnames whose section numbers changed

View File

@ -27,7 +27,8 @@ from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError from sphinx.application import ExtensionError
from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive from sphinx.util.compat import Directive
from sphinx.util.inspect import isdescriptor, safe_getmembers, safe_getattr from sphinx.util.inspect import (getargspec, isdescriptor, safe_getmembers, \
safe_getattr)
from sphinx.util.pycompat import base_exception, class_types from sphinx.util.pycompat import base_exception, class_types
from sphinx.util.docstrings import prepare_docstring from sphinx.util.docstrings import prepare_docstring
@ -1284,36 +1285,6 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls AutoDirective._registry[cls.objtype] = cls
if sys.version_info >= (2, 5):
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.im_func
parts = 0, ()
if type(func) is partial:
parts = len(func.args), func.keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('{!r} is not a Python function'.format(func))
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
if func_defaults:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
else:
getargspec = inspect.getargspec
def setup(app): def setup(app):
app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ModuleDocumenter)
app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ClassDocumenter)

View File

@ -361,6 +361,8 @@ PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \
$(SPHINXOPTS) %(rsrcdir)s $(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 \ .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \
epub latex latexpdf text man changes linkcheck doctest gettext 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." \t@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext: gettext:
\t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale \t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
\t@echo \t@echo
\t@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." \t@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
@ -514,8 +516,10 @@ if "%%SPHINXBUILD%%" == "" (
) )
set BUILDDIR=%(rbuilddir)s set BUILDDIR=%(rbuilddir)s
set ALLSPHINXOPTS=-d %%BUILDDIR%%/doctrees %%SPHINXOPTS%% %(rsrcdir)s set ALLSPHINXOPTS=-d %%BUILDDIR%%/doctrees %%SPHINXOPTS%% %(rsrcdir)s
set I18NSPHINXOPTS=%%SPHINXOPTS%% %(rsrcdir)s
if NOT "%%PAPER%%" == "" ( if NOT "%%PAPER%%" == "" (
\tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%% \tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%%
\tset I18NSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%I18NSPHINXOPTS%%
) )
if "%%1" == "" goto help if "%%1" == "" goto help
@ -659,7 +663,7 @@ if "%%1" == "texinfo" (
) )
if "%%1" == "gettext" ( if "%%1" == "gettext" (
\t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale \t%%SPHINXBUILD%% -b gettext %%I18NSPHINXOPTS%% %%BUILDDIR%%/locale
\tif errorlevel 1 exit /b 1 \tif errorlevel 1 exit /b 1
\techo. \techo.
\techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale. \techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale.
@ -991,4 +995,3 @@ def main(argv=sys.argv):
print print
print '[Interrupted.]' print '[Interrupted.]'
return return

View File

@ -69,9 +69,10 @@ class XRefRole(object):
innernodeclass = nodes.literal innernodeclass = nodes.literal
def __init__(self, fix_parens=False, lowercase=False, 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.fix_parens = fix_parens
self.lowercase = lowercase self.lowercase = lowercase
self.warn_dangling = warn_dangling
if nodeclass is not None: if nodeclass is not None:
self.nodeclass = nodeclass self.nodeclass = nodeclass
if innernodeclass is not None: if innernodeclass is not None:
@ -133,6 +134,7 @@ class XRefRole(object):
refnode += self.innernodeclass(rawtext, title, classes=classes) refnode += self.innernodeclass(rawtext, title, classes=classes)
# we also need the source document # we also need the source document
refnode['refdoc'] = env.docname refnode['refdoc'] = env.docname
refnode['refwarn'] = self.warn_dangling
# result_nodes allow further modification of return values # result_nodes allow further modification of return values
return self.result_nodes(inliner.document, env, refnode, is_ref=True) return self.result_nodes(inliner.document, env, refnode, is_ref=True)
@ -298,7 +300,7 @@ specific_docroles = {
# links to download references # links to download references
'download': XRefRole(nodeclass=addnodes.download_reference), 'download': XRefRole(nodeclass=addnodes.download_reference),
# links to documents # links to documents
'doc': XRefRole(), 'doc': XRefRole(warn_dangling=True),
'pep': indexmarkup_role, 'pep': indexmarkup_role,
'rfc': indexmarkup_role, 'rfc': indexmarkup_role,

View File

@ -9,6 +9,40 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
inspect = __import__('inspect')
import sys
if sys.version_info >= (2, 5):
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.im_func
parts = 0, ()
if type(func) is partial:
parts = len(func.args), func.keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
if func_defaults:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
else:
getargspec = inspect.getargspec
def isdescriptor(x): def isdescriptor(x):
"""Check if the object is some kind of descriptor.""" """Check if the object is some kind of descriptor."""
for item in '__get__', '__set__', '__delete__': for item in '__get__', '__set__', '__delete__':

View File

@ -11,7 +11,6 @@
""" """
from uuid import uuid4 from uuid import uuid4
from operator import itemgetter from operator import itemgetter
from collections import defaultdict
from sphinx.util.pycompat import product, zip_longest from sphinx.util.pycompat import product, zip_longest
@ -49,7 +48,7 @@ def merge_doctrees(old, new, condition):
new_iter = new.traverse(condition) new_iter = new.traverse(condition)
old_nodes = [] old_nodes = []
new_nodes = [] new_nodes = []
ratios = defaultdict(list) ratios = {}
seen = set() seen = set()
# compare the nodes each doctree in order # compare the nodes each doctree in order
for old_node, new_node in zip_longest(old_iter, new_iter): for old_node, new_node in zip_longest(old_iter, new_iter):