Let docutils know the location of `docutils.conf` for Sphinx

This commit is contained in:
Takeshi KOMIYA 2018-03-29 22:45:20 +09:00
parent d80801deeb
commit 81946e423a
6 changed files with 63 additions and 26 deletions

View File

@ -23,6 +23,8 @@ Incompatible changes
:py:meth:`.Sphinx.add_transform()`
* #4827: All ``substitution_definition`` nodes are removed from doctree on
reading phase
* ``docutils.conf`` on ``$HOME`` and ``/etc`` directories are ignored. Only
``docutils.conf`` on confdir is refered.
Deprecated
----------

View File

@ -293,7 +293,8 @@ def build_main(argv=sys.argv[1:]): # type: ignore
app = None
try:
with patch_docutils(), docutils_namespace():
confdir = args.confdir or args.sourcedir
with patch_docutils(confdir), docutils_namespace():
app = Sphinx(args.sourcedir, args.confdir, args.outputdir,
args.doctreedir, args.builder, confoverrides, status,
warning, args.freshenv, args.warningiserror,

View File

@ -19,7 +19,6 @@ from collections import defaultdict
from copy import copy
from os import path
from docutils.frontend import OptionParser
from docutils.utils import Reporter, get_source_line
from six import BytesIO, class_types, next
from six.moves import cPickle as pickle
@ -561,9 +560,8 @@ class BuildEnvironment(object):
"""Parse a file and add/update inventory entries for the doctree."""
self.prepare_settings(docname)
docutilsconf = path.join(self.srcdir, 'docutils.conf')
# read docutils.conf from source dir, not from current dir
OptionParser.standard_config_files[1] = docutilsconf
# Add confdir/docutils.conf to dependencies list if exists
docutilsconf = path.join(self.app.confdir, 'docutils.conf')
if path.isfile(docutilsconf):
self.note_dependency(docutilsconf)

View File

@ -179,7 +179,8 @@ class BuildDoc(Command):
app = None
try:
with patch_docutils(), docutils_namespace():
confdir = self.config_dir or self.source_dir
with patch_docutils(confdir), docutils_namespace():
app = Sphinx(self.source_dir, self.config_dir,
builder_target_dir, self.doctree_dir,
builder, confoverrides, status_stream,

View File

@ -10,16 +10,17 @@
"""
from __future__ import absolute_import
import os
import re
import types
import warnings
from contextlib import contextmanager
from copy import copy
from distutils.version import LooseVersion
from os import path
import docutils
from docutils import nodes
from docutils.languages import get_language
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
from docutils.statemachine import StateMachine
from docutils.utils import Reporter
@ -34,7 +35,7 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False:
# For type annotation
from typing import Any, Callable, Generator, Iterator, List, Set, Tuple # NOQA
from typing import Any, Callable, Generator, List, Set, Tuple # NOQA
from docutils.statemachine import State, ViewList # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -47,7 +48,7 @@ additional_nodes = set() # type: Set[nodes.Node]
@contextmanager
def docutils_namespace():
# type: () -> Iterator[None]
# type: () -> Generator[None, None, None]
"""Create namespace for reST parsers."""
try:
_directives = copy(directives._directives)
@ -94,29 +95,53 @@ def unregister_node(node):
delattr(nodes.SparseNodeVisitor, 'depart_' + node.__name__)
def patched_get_language(language_code, reporter=None):
# type: (unicode, Reporter) -> Any
"""A wrapper for docutils.languages.get_language().
@contextmanager
def patched_get_language():
# type: () -> Generator[None, None, None]
"""Patch docutils.languages.get_language() temporarily.
This ignores the second argument ``reporter`` to suppress warnings.
refs: https://github.com/sphinx-doc/sphinx/issues/3788
"""
return get_language(language_code)
from docutils.languages import get_language
def patched_get_language(language_code, reporter=None):
# type: (unicode, Reporter) -> Any
return get_language(language_code)
@contextmanager
def patch_docutils():
# type: () -> Iterator[None]
"""Patch to docutils temporarily."""
try:
docutils.languages.get_language = patched_get_language
yield
finally:
# restore original implementations
docutils.languages.get_language = get_language
@contextmanager
def using_user_docutils_conf(confdir):
# type: (unicode) -> Generator[None, None, None]
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
if confdir:
os.environ['DOCUTILSCONFIG'] = path.join(path.abspath(confdir), 'docutils.conf') # type: ignore # NOQA
yield
finally:
if docutilsconfig is None:
os.environ.pop('DOCUTILSCONFIG')
else:
os.environ['DOCUTILSCONFIG'] = docutilsconfig
@contextmanager
def patch_docutils(confdir=None):
# type: (unicode) -> Generator[None, None, None]
"""Patch to docutils temporarily."""
with patched_get_language(), using_user_docutils_conf(confdir):
yield
class ElementLookupError(Exception):
pass
@ -257,7 +282,7 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
@contextmanager
def switch_source_input(state, content):
# type: (State, ViewList) -> Generator
# type: (State, ViewList) -> Generator[None, None, None]
"""Switch current source input of state temporarily."""
try:
# remember the original ``get_source_and_line()`` method

View File

@ -15,6 +15,7 @@ import sys
import pytest
from sphinx.testing.path import path
from sphinx.util.docutils import patch_docutils
def regex_count(expr, result):
@ -23,7 +24,9 @@ def regex_count(expr, result):
@pytest.mark.sphinx('html', testroot='docutilsconf', freshenv=True, docutilsconf='')
def test_html_with_default_docutilsconf(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert regex_count(r'<th class="field-name">', result) == 1
@ -39,7 +42,9 @@ def test_html_with_default_docutilsconf(app, status, warning):
'\n')
)
def test_html_with_docutilsconf(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert regex_count(r'<th class="field-name">', result) == 0
@ -50,25 +55,29 @@ def test_html_with_docutilsconf(app, status, warning):
@pytest.mark.sphinx('html', testroot='docutilsconf')
def test_html(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == ''
@pytest.mark.sphinx('latex', testroot='docutilsconf')
def test_latex(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == ''
@pytest.mark.sphinx('man', testroot='docutilsconf')
def test_man(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == ''
@pytest.mark.sphinx('texinfo', testroot='docutilsconf')
def test_texinfo(app, status, warning):
app.builder.build(['contents'])
with patch_docutils(app.confdir):
app.builder.build(['contents'])
@pytest.mark.sphinx('html', testroot='docutilsconf',
@ -87,4 +96,5 @@ def test_docutils_source_link_with_nonascii_file(app, status, warning):
'nonascii filename not supported on this filesystem encoding: '
'%s', FILESYSTEMENCODING)
app.builder.build_all()
with patch_docutils(app.confdir):
app.builder.build_all()