summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2019-05-13 22:03:26 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2019-05-13 22:03:26 +0900
commitf63abac2cad2664a8af816017f0f997bae510d14 (patch)
tree27d641b3893424b1d3226c495ede57d3a6f85a6e /sphinx
parent165897a74951fb03e497d6e05496ce02e897f820 (diff)
parent3e5adaa2252326681d33c09c1585e83e54072ffb (diff)
downloadsphinx-git-f63abac2cad2664a8af816017f0f997bae510d14.tar.gz
Merge branch '2.0'
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/domains/python.py20
-rw-r--r--sphinx/domains/rst.py76
-rw-r--r--sphinx/ext/autodoc/__init__.py43
-rw-r--r--sphinx/ext/autodoc/directive.py22
-rw-r--r--sphinx/ext/autosummary/__init__.py4
-rw-r--r--sphinx/ext/autosummary/generate.py18
-rw-r--r--sphinx/highlighting.py42
-rw-r--r--sphinx/io.py11
-rw-r--r--sphinx/templates/latex/longtable.tex_t2
-rw-r--r--sphinx/templates/latex/tabular.tex_t2
-rw-r--r--sphinx/templates/latex/tabulary.tex_t2
-rw-r--r--sphinx/themes/basic/searchbox.html4
-rw-r--r--sphinx/themes/basic/static/basic.css_t15
-rw-r--r--sphinx/transforms/__init__.py2
-rw-r--r--sphinx/transforms/references.py19
-rw-r--r--sphinx/util/docstrings.py6
-rw-r--r--sphinx/util/inspect.py6
-rw-r--r--sphinx/writers/latex.py1
-rw-r--r--sphinx/writers/manpage.py2
19 files changed, 244 insertions, 53 deletions
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index c0bef26a5..4bfaaf848 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -578,22 +578,28 @@ class PyMethod(PyObject):
option_spec.update({
'async': directives.flag,
'classmethod': directives.flag,
+ 'property': directives.flag,
'staticmethod': directives.flag,
})
def needs_arglist(self):
# type: () -> bool
- return True
+ if 'property' in self.options:
+ return False
+ else:
+ return True
def get_signature_prefix(self, sig):
# type: (str) -> str
prefix = []
if 'async' in self.options:
prefix.append('async')
- if 'staticmethod' in self.options:
- prefix.append('static')
if 'classmethod' in self.options:
prefix.append('classmethod')
+ if 'property' in self.options:
+ prefix.append('property')
+ if 'staticmethod' in self.options:
+ prefix.append('static')
if prefix:
return ' '.join(prefix) + ' '
@@ -613,10 +619,12 @@ class PyMethod(PyObject):
else:
return '%s()' % name
- if 'staticmethod' in self.options:
- return _('%s() (%s static method)') % (methname, clsname)
- elif 'classmethod' in self.options:
+ if 'classmethod' in self.options:
return _('%s() (%s class method)') % (methname, clsname)
+ elif 'property' in self.options:
+ return _('%s() (%s property)') % (methname, clsname)
+ elif 'staticmethod' in self.options:
+ return _('%s() (%s static method)') % (methname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index bccad0628..f054abf28 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -11,6 +11,8 @@
import re
from typing import cast
+from docutils.parsers.rst import directives
+
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
@@ -98,6 +100,74 @@ class ReSTDirective(ReSTMarkup):
# type: (str, str) -> str
return _('%s (directive)') % name
+ def before_content(self):
+ # type: () -> None
+ if self.names:
+ directives = self.env.ref_context.setdefault('rst:directives', [])
+ directives.append(self.names[0])
+
+ def after_content(self):
+ # type: () -> None
+ directives = self.env.ref_context.setdefault('rst:directives', [])
+ if directives:
+ directives.pop()
+
+
+class ReSTDirectiveOption(ReSTMarkup):
+ """
+ Description of an option for reST directive.
+ """
+ option_spec = ReSTMarkup.option_spec.copy()
+ option_spec.update({
+ 'type': directives.unchanged,
+ })
+
+ def handle_signature(self, sig, signode):
+ # type: (str, addnodes.desc_signature) -> str
+ try:
+ name, argument = re.split(r'\s*:\s+', sig.strip(), 1)
+ except ValueError:
+ name, argument = sig, None
+
+ signode += addnodes.desc_name(':%s:' % name, ':%s:' % name)
+ if argument:
+ signode += addnodes.desc_annotation(' ' + argument, ' ' + argument)
+ if self.options.get('type'):
+ text = ' (%s)' % self.options['type']
+ signode += addnodes.desc_annotation(text, text)
+ return name
+
+ def add_target_and_index(self, name, sig, signode):
+ # type: (str, str, addnodes.desc_signature) -> None
+ targetname = '-'.join([self.objtype, self.current_directive, name])
+ if targetname not in self.state.document.ids:
+ signode['names'].append(targetname)
+ signode['ids'].append(targetname)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ domain = cast(ReSTDomain, self.env.get_domain('rst'))
+ domain.note_object(self.objtype, name, location=(self.env.docname, self.lineno))
+
+ if self.current_directive:
+ key = name[0].upper()
+ pair = [_('%s (directive)') % self.current_directive,
+ _(':%s: (directive option)') % name]
+ self.indexnode['entries'].append(('pair', '; '.join(pair), targetname, '', key))
+ else:
+ key = name[0].upper()
+ text = _(':%s: (directive option)') % name
+ self.indexnode['entries'].append(('single', text, targetname, '', key))
+
+ @property
+ def current_directive(self):
+ # type: () -> str
+ directives = self.env.ref_context.get('rst:directives')
+ if directives:
+ return directives[-1]
+ else:
+ return ''
+
class ReSTRole(ReSTMarkup):
"""
@@ -119,11 +189,13 @@ class ReSTDomain(Domain):
label = 'reStructuredText'
object_types = {
- 'directive': ObjType(_('directive'), 'dir'),
- 'role': ObjType(_('role'), 'role'),
+ 'directive': ObjType(_('directive'), 'dir'),
+ 'directive:option': ObjType(_('directive-option'), 'dir'),
+ 'role': ObjType(_('role'), 'role'),
}
directives = {
'directive': ReSTDirective,
+ 'directive:option': ReSTDirectiveOption,
'role': ReSTRole,
}
roles = {
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 6343022c2..b3c04e464 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -440,7 +440,8 @@ class Documenter:
docstring = getdoc(self.object, self.get_attr,
self.env.config.autodoc_inherit_docstrings)
if docstring:
- return [prepare_docstring(docstring, ignore)]
+ tab_width = self.directive.state.document.settings.tab_width
+ return [prepare_docstring(docstring, ignore, tab_width)]
return []
def process_doc(self, docstrings):
@@ -934,7 +935,9 @@ class DocstringSignatureMixin:
if base not in valid_names:
continue
# re-prepare docstring to ignore more leading indentation
- self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[1:]))
+ tab_width = self.directive.state.document.settings.tab_width # type: ignore
+ self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[1:]),
+ tabsize=tab_width)
result = args, retann
# don't look any further
break
@@ -1177,7 +1180,9 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
docstrings = [initdocstring]
else:
docstrings.append(initdocstring)
- return [prepare_docstring(docstring, ignore) for docstring in docstrings]
+
+ tab_width = self.directive.state.document.settings.tab_width
+ return [prepare_docstring(docstring, ignore, tab_width) for docstring in docstrings]
def add_content(self, more_content, no_docstring=False):
# type: (Any, bool) -> None
@@ -1413,6 +1418,37 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
super().add_content(more_content, no_docstring)
+class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
+ """
+ Specialized Documenter subclass for properties.
+ """
+ objtype = 'property'
+ directivetype = 'method'
+ member_order = 60
+
+ # before AttributeDocumenter
+ priority = AttributeDocumenter.priority + 1
+
+ @classmethod
+ def can_document_member(cls, member, membername, isattr, parent):
+ # type: (Any, str, bool, Any) -> bool
+ return inspect.isproperty(member) and isinstance(parent, ClassDocumenter)
+
+ def document_members(self, all_members=False):
+ # type: (bool) -> None
+ pass
+
+ def get_real_modname(self):
+ # type: () -> str
+ return self.get_attr(self.parent or self.object, '__module__', None) \
+ or self.modname
+
+ def add_directive_header(self, sig):
+ # type: (str) -> None
+ super().add_directive_header(sig)
+ self.add_line(' :property:', self.get_sourcename())
+
+
class InstanceAttributeDocumenter(AttributeDocumenter):
"""
Specialized Documenter subclass for attributes that cannot be imported
@@ -1471,6 +1507,7 @@ def setup(app):
app.add_autodocumenter(DecoratorDocumenter)
app.add_autodocumenter(MethodDocumenter)
app.add_autodocumenter(AttributeDocumenter)
+ app.add_autodocumenter(PropertyDocumenter)
app.add_autodocumenter(InstanceAttributeDocumenter)
app.add_config_value('autoclass_content', 'class', True)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 42415433b..6b002b101 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -6,10 +6,14 @@
:license: BSD, see LICENSE for details.
"""
+import warnings
+
from docutils import nodes
+from docutils.parsers.rst.states import Struct
from docutils.statemachine import StringList
from docutils.utils import assemble_option_dict
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.ext.autodoc import Options, get_documenters
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, switch_source_input
@@ -17,7 +21,7 @@ from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
- from typing import Callable, Dict, List, Set, Type # NOQA
+ from typing import Any, Callable, Dict, List, Set, Type # NOQA
from docutils.parsers.rst.state import RSTState # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.config import Config # NOQA
@@ -50,8 +54,8 @@ class DummyOptionSpec(dict):
class DocumenterBridge:
"""A parameters container for Documenters."""
- def __init__(self, env, reporter, options, lineno):
- # type: (BuildEnvironment, Reporter, Options, int) -> None
+ def __init__(self, env, reporter, options, lineno, state=None):
+ # type: (BuildEnvironment, Reporter, Options, int, Any) -> None
self.env = env
self.reporter = reporter
self.genopt = options
@@ -59,6 +63,16 @@ class DocumenterBridge:
self.filename_set = set() # type: Set[str]
self.result = StringList()
+ if state:
+ self.state = state
+ else:
+ # create fake object for self.state.document.settings.tab_width
+ warnings.warn('DocumenterBridge requires a state object on instantiation.',
+ RemovedInSphinx40Warning)
+ settings = Struct(tab_width=8)
+ document = Struct(settings=settings)
+ self.state = Struct(document=document)
+
def warn(self, msg):
# type: (str) -> None
logger.warning(msg, location=(self.env.docname, self.lineno))
@@ -131,7 +145,7 @@ class AutodocDirective(SphinxDirective):
return []
# generate the output
- params = DocumenterBridge(self.env, reporter, documenter_options, lineno)
+ params = DocumenterBridge(self.env, reporter, documenter_options, lineno, self.state)
documenter = doccls(params, self.arguments[0])
documenter.generate(more_content=self.content)
if not params.result:
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 952bd9e2a..5840f0ccd 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -175,7 +175,7 @@ _app = None # type: Sphinx
class FakeDirective(DocumenterBridge):
def __init__(self):
# type: () -> None
- super().__init__({}, None, Options(), 0) # type: ignore
+ super().__init__({}, None, Options(), 0, None) # type: ignore
def get_documenter(app, obj, parent):
@@ -236,7 +236,7 @@ class Autosummary(SphinxDirective):
def run(self):
# type: () -> List[nodes.Node]
self.bridge = DocumenterBridge(self.env, self.state.document.reporter,
- Options(), self.lineno)
+ Options(), self.lineno, self.state)
names = [x.strip().split()[0] for x in self.content
if x.strip() and re.search(r'^[~a-zA-Z_]', x.strip()[0])]
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 1e9dbedc8..eac25697a 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -40,7 +40,7 @@ from sphinx.util.rst import escape as rst_escape
if False:
# For type annotation
- from typing import Any, Callable, Dict, List, Tuple, Type, Union # NOQA
+ from typing import Any, Callable, Dict, List, Set, Tuple, Type, Union # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.ext.autodoc import Documenter # NOQA
@@ -169,8 +169,8 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
except TemplateNotFound:
template = template_env.get_template('autosummary/base.rst')
- def get_members(obj, typ, include_public=[], imported=True):
- # type: (Any, str, List[str], bool) -> Tuple[List[str], List[str]]
+ def get_members(obj, types, include_public=[], imported=True):
+ # type: (Any, Set[str], List[str], bool) -> Tuple[List[str], List[str]] # NOQA
items = [] # type: List[str]
for name in dir(obj):
try:
@@ -178,7 +178,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
except AttributeError:
continue
documenter = get_documenter(app, value, obj)
- if documenter.objtype == typ:
+ if documenter.objtype in types:
if imported or getattr(value, '__module__', None) == obj.__name__:
# skip imported members if expected
items.append(name)
@@ -191,19 +191,19 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
if doc.objtype == 'module':
ns['members'] = dir(obj)
ns['functions'], ns['all_functions'] = \
- get_members(obj, 'function', imported=imported_members)
+ get_members(obj, {'function'}, imported=imported_members)
ns['classes'], ns['all_classes'] = \
- get_members(obj, 'class', imported=imported_members)
+ get_members(obj, {'class'}, imported=imported_members)
ns['exceptions'], ns['all_exceptions'] = \
- get_members(obj, 'exception', imported=imported_members)
+ get_members(obj, {'exception'}, imported=imported_members)
elif doc.objtype == 'class':
ns['members'] = dir(obj)
ns['inherited_members'] = \
set(dir(obj)) - set(obj.__dict__.keys())
ns['methods'], ns['all_methods'] = \
- get_members(obj, 'method', ['__init__'])
+ get_members(obj, {'method'}, ['__init__'])
ns['attributes'], ns['all_attributes'] = \
- get_members(obj, 'attribute')
+ get_members(obj, {'attribute', 'property'})
parts = name.split('.')
if doc.objtype in ('method', 'attribute'):
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index c194738f8..995ca65d3 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -27,6 +27,7 @@ if False:
# For type annotation
from typing import Any, Dict # NOQA
from pygments.formatter import Formatter # NOQA
+ from pygments.style import Style # NOQA
logger = logging.getLogger(__name__)
@@ -64,16 +65,8 @@ class PygmentsBridge:
def __init__(self, dest='html', stylename='sphinx'):
# type: (str, str) -> None
self.dest = dest
- if stylename is None or stylename == 'sphinx':
- style = SphinxStyle
- elif stylename == 'none':
- style = NoneStyle
- elif '.' in stylename:
- module, stylename = stylename.rsplit('.', 1)
- style = getattr(__import__(module, None, None, ['__name__']),
- stylename)
- else:
- style = get_style_by_name(stylename)
+
+ style = self.get_style(stylename)
self.formatter_args = {'style': style} # type: Dict[str, Any]
if dest == 'html':
self.formatter = self.html_formatter
@@ -81,16 +74,25 @@ class PygmentsBridge:
self.formatter = self.latex_formatter
self.formatter_args['commandprefix'] = 'PYG'
+ def get_style(self, stylename):
+ # type: (str) -> Style
+ if stylename is None or stylename == 'sphinx':
+ return SphinxStyle
+ elif stylename == 'none':
+ return NoneStyle
+ elif '.' in stylename:
+ module, stylename = stylename.rsplit('.', 1)
+ return getattr(__import__(module, None, None, ['__name__']), stylename)
+ else:
+ return get_style_by_name(stylename)
+
def get_formatter(self, **kwargs):
# type: (Any) -> Formatter
kwargs.update(self.formatter_args)
return self.formatter(**kwargs)
- def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
- # type: (str, str, Any, Any, bool, Any) -> str
- if not isinstance(source, str):
- source = source.decode()
-
+ def get_lexer(self, source, lang, opts=None, location=None):
+ # type: (str, str, Any, Any) -> Lexer
# find out which lexer to use
if lang in ('py', 'python'):
if source.startswith('>>>'):
@@ -121,6 +123,15 @@ class PygmentsBridge:
else:
lexer.add_filter('raiseonerror')
+ return lexer
+
+ def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
+ # type: (str, str, Any, Any, bool, Any) -> str
+ if not isinstance(source, str):
+ source = source.decode()
+
+ lexer = self.get_lexer(source, lang, opts, location)
+
# highlight via Pygments
formatter = self.get_formatter(**kwargs)
try:
@@ -136,6 +147,7 @@ class PygmentsBridge:
type='misc', subtype='highlighting_failure',
location=location)
hlsource = highlight(source, lexers['none'], formatter)
+
if self.dest == 'html':
return hlsource
else:
diff --git a/sphinx/io.py b/sphinx/io.py
index 105ed4397..354121c86 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -14,6 +14,7 @@ from docutils.core import Publisher
from docutils.io import FileInput, NullOutput
from docutils.parsers.rst import Parser as RSTParser
from docutils.readers import standalone
+from docutils.transforms.references import DanglingReferences
from docutils.writers import UnfilteredWriter
from sphinx.transforms import (
@@ -60,7 +61,15 @@ class SphinxBaseReader(standalone.Reader):
def get_transforms(self):
# type: () -> List[Type[Transform]]
- return super().get_transforms() + self.transforms
+ transforms = super().get_transforms() + self.transforms
+
+ # remove transforms which is not needed for Sphinx
+ unused = [DanglingReferences]
+ for transform in unused:
+ if transform in transforms:
+ transforms.remove(transform)
+
+ return transforms
def new_document(self):
# type: () -> nodes.document
diff --git a/sphinx/templates/latex/longtable.tex_t b/sphinx/templates/latex/longtable.tex_t
index ade1a54af..8fe5369df 100644
--- a/sphinx/templates/latex/longtable.tex_t
+++ b/sphinx/templates/latex/longtable.tex_t
@@ -1,5 +1,5 @@
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}
-<%- if table.align == 'center' -%>
+<%- if table.align in ('center', 'default') -%>
[c]
<%- elif table.align == 'left' -%>
[l]
diff --git a/sphinx/templates/latex/tabular.tex_t b/sphinx/templates/latex/tabular.tex_t
index a4f56feb3..a0db7faff 100644
--- a/sphinx/templates/latex/tabular.tex_t
+++ b/sphinx/templates/latex/tabular.tex_t
@@ -1,6 +1,6 @@
\begin{savenotes}\sphinxattablestart
<% if table.align -%>
- <%- if table.align == 'center' -%>
+ <%- if table.align in ('center', 'default') -%>
\centering
<%- elif table.align == 'left' -%>
\raggedright
diff --git a/sphinx/templates/latex/tabulary.tex_t b/sphinx/templates/latex/tabulary.tex_t
index e3534725b..3236b798a 100644
--- a/sphinx/templates/latex/tabulary.tex_t
+++ b/sphinx/templates/latex/tabulary.tex_t
@@ -1,6 +1,6 @@
\begin{savenotes}\sphinxattablestart
<% if table.align -%>
- <%- if table.align == 'center' -%>
+ <%- if table.align in ('center', 'default') -%>
\centering
<%- elif table.align == 'left' -%>
\raggedright
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index 2ed7fa137..6679ca6b5 100644
--- a/sphinx/themes/basic/searchbox.html
+++ b/sphinx/themes/basic/searchbox.html
@@ -9,10 +9,10 @@
#}
{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
- <h3>{{ _('Quick search') }}</h3>
+ <h3 id="searchlabel">{{ _('Quick search') }}</h3>
<div class="searchformwrapper">
<form class="search" action="{{ pathto('search') }}" method="get">
- <input type="text" name="q" />
+ <input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="{{ _('Go') }}" />
</form>
</div>
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index 90a14286f..91fd35755 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -289,6 +289,12 @@ img.align-center, .figure.align-center, object.align-center {
margin-right: auto;
}
+img.align-default, .figure.align-default {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
.align-left {
text-align: left;
}
@@ -297,6 +303,10 @@ img.align-center, .figure.align-center, object.align-center {
text-align: center;
}
+.align-default {
+ text-align: center;
+}
+
.align-right {
text-align: right;
}
@@ -368,6 +378,11 @@ table.align-center {
margin-right: auto;
}
+table.align-default {
+ margin-left: auto;
+ margin-right: auto;
+}
+
table caption span.caption-number {
font-style: italic;
}
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index 16849c46c..a4e6e52bf 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -293,7 +293,7 @@ class FigureAligner(SphinxTransform):
# type: (Any) -> None
matcher = NodeMatcher(nodes.table, nodes.figure)
for node in self.document.traverse(matcher): # type: nodes.Element
- node.setdefault('align', 'center')
+ node.setdefault('align', 'default')
class FilterSystemMessages(SphinxTransform):
diff --git a/sphinx/transforms/references.py b/sphinx/transforms/references.py
index de512f437..9cdc28c78 100644
--- a/sphinx/transforms/references.py
+++ b/sphinx/transforms/references.py
@@ -9,7 +9,7 @@
"""
from docutils import nodes
-from docutils.transforms.references import Substitutions
+from docutils.transforms.references import DanglingReferences, Substitutions
from sphinx.transforms import SphinxTransform
@@ -31,6 +31,22 @@ class SubstitutionDefinitionsRemover(SphinxTransform):
node.parent.remove(node)
+class SphinxDanglingReferences(DanglingReferences):
+ """DanglingReferences transform which does not output info messages."""
+
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ try:
+ reporter = self.document.reporter
+ report_level = reporter.report_level
+
+ # suppress INFO level messages for a while
+ reporter.report_level = max(reporter.WARNING_LEVEL, reporter.report_level)
+ super().apply()
+ finally:
+ reporter.report_level = report_level
+
+
class SphinxDomains(SphinxTransform):
"""Collect objects to Sphinx domains for cross references."""
default_priority = 850
@@ -44,6 +60,7 @@ class SphinxDomains(SphinxTransform):
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_transform(SubstitutionDefinitionsRemover)
+ app.add_transform(SphinxDanglingReferences)
app.add_transform(SphinxDomains)
return {
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index 97dd60294..31943b2cb 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -15,8 +15,8 @@ if False:
from typing import List # NOQA
-def prepare_docstring(s, ignore=1):
- # type: (str, int) -> List[str]
+def prepare_docstring(s, ignore=1, tabsize=8):
+ # type: (str, int, int) -> List[str]
"""Convert a docstring into lines of parseable reST. Remove common leading
indentation, where the indentation of a given number of lines (usually just
one) is ignored.
@@ -25,7 +25,7 @@ def prepare_docstring(s, ignore=1):
ViewList (used as argument of nested_parse().) An empty line is added to
act as a separator between this docstring and following content.
"""
- lines = s.expandtabs().splitlines()
+ lines = s.expandtabs(tabsize).splitlines()
# Find minimum indentation of any non-blank lines after ignored lines.
margin = sys.maxsize
for line in lines[ignore:]:
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 5ba05f04b..caf333493 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -222,6 +222,12 @@ def iscoroutinefunction(obj):
return False
+def isproperty(obj):
+ # type: (Any) -> bool
+ """Check if the object is property."""
+ return isinstance(obj, property)
+
+
def safe_getattr(obj, name, *defargs):
# type: (Any, str, str) -> object
"""A getattr() that turns all exceptions into AttributeErrors."""
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index b45a95eef..803dd1dbc 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1527,6 +1527,7 @@ class LaTeXTranslator(SphinxTranslator):
(1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
(1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
(0, 'center'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
+ (0, 'default'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
# These 2 don't exactly do the right thing. The image should
# be floated alongside the paragraph. See
# https://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 0856ee5ee..7811ccc5b 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -282,7 +282,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_rubric(self, node):
# type: (nodes.Element) -> None
- pass
+ self.body.append('\n')
def visit_seealso(self, node):
# type: (nodes.Element) -> None