summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2019-03-24 01:22:30 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2019-04-25 21:44:44 +0900
commit1ea23e14df871ff97aa4082dddecfd11c4465cbe (patch)
tree9be4792098d586afa0d18af089cb6ea9dd305fdc
parentde0c44196e6c9f8ea9ec5dd58098f83dae93277d (diff)
downloadsphinx-git-1ea23e14df871ff97aa4082dddecfd11c4465cbe.tar.gz
Fix #6165: autodoc: ``tab_width`` setting of docutils has been ignored
-rw-r--r--CHANGES1
-rw-r--r--sphinx/ext/autodoc/__init__.py11
-rw-r--r--sphinx/ext/autodoc/directive.py22
-rw-r--r--sphinx/ext/autosummary/__init__.py4
-rw-r--r--sphinx/util/docstrings.py6
-rw-r--r--tests/test_autodoc.py7
6 files changed, 38 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index 9cc8cab22..a8ea6c387 100644
--- a/CHANGES
+++ b/CHANGES
@@ -91,6 +91,7 @@ Bugs fixed
* #6213: ifconfig: contents after headings are not shown
* commented term in glossary directive is wrongly recognized
* #6299: rst domain: rst:directive directive generates waste space
+* #6165: autodoc: ``tab_width`` setting of docutils has been ignored
Testing
--------
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 2a4df2159..412c336e3 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -442,7 +442,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):
@@ -936,7 +937,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
@@ -1179,7 +1182,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
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/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/tests/test_autodoc.py b/tests/test_autodoc.py
index 5f616b791..75d59db14 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -11,6 +11,7 @@
import platform
import sys
+from unittest.mock import Mock
from warnings import catch_warnings
import pytest
@@ -36,7 +37,9 @@ def do_autodoc(app, objtype, name, options=None):
app.env.temp_data.setdefault('docname', 'index') # set dummy docname
doccls = app.registry.documenters[objtype]
docoptions = process_documenter_options(doccls, app.config, options)
- bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1)
+ state = Mock()
+ state.document.settings.tab_width = 8
+ bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1, state)
documenter = doccls(bridge, name)
documenter.generate()
@@ -95,7 +98,9 @@ def setup_test():
genopt = options,
result = ViewList(),
filename_set = set(),
+ state = Mock(),
)
+ directive.state.document.settings.tab_width = 8
processed_docstrings = []
processed_signatures = []