Strip ANSI control codes when writing to the warnings file (#11624)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Bénédikt Tran 2023-09-21 11:01:07 +02:00 committed by GitHub
parent b935915c57
commit 5fe0bd41eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 3 deletions

View File

@ -20,6 +20,9 @@ Bugs fixed
Patch by Vinay Sajip.
* #11622: Ensure that the order of keys in ``searchindex.js`` is deterministic.
Patch by Pietro Albini.
* #11617: ANSI control sequences are stripped from the output when writing to
a warnings file with :option:`-w <sphinx-build -w>`.
Patch by Bénédikt Tran.
Testing
-------

View File

@ -195,6 +195,10 @@ Options
Write warnings (and errors) to the given file, in addition to standard error.
.. versionchanged:: 7.3
ANSI control sequences are stripped when writing to *file*.
.. option:: -W
Turn warnings into errors. This means that the build stops at the first

View File

@ -21,7 +21,7 @@ from sphinx import __display_version__
from sphinx.application import Sphinx
from sphinx.errors import SphinxError, SphinxParallelError
from sphinx.locale import __
from sphinx.util import Tee
from sphinx.util._io import TeeStripANSI
from sphinx.util.console import ( # type: ignore[attr-defined]
color_terminal,
nocolor,
@ -34,6 +34,11 @@ from sphinx.util.osutil import ensuredir
if TYPE_CHECKING:
from collections.abc import Sequence
from typing import Protocol
class SupportsWrite(Protocol):
def write(self, text: str, /) -> int | None:
...
def handle_exception(
@ -266,11 +271,12 @@ def _parse_logging(
try:
warnfile = path.abspath(warnfile)
ensuredir(path.dirname(warnfile))
# the caller is responsible for closing this file descriptor
warnfp = open(warnfile, 'w', encoding="utf-8") # NoQA: SIM115
except Exception as exc:
parser.error(__('cannot open warning file %r: %s') % (
warnfile, exc))
warning = Tee(warning, warnfp) # type: ignore[assignment]
warning = TeeStripANSI(warning, warnfp) # type: ignore[assignment]
error = warning
return status, warning, error, warnfp
@ -334,6 +340,10 @@ def build_main(argv: Sequence[str]) -> int:
except (Exception, KeyboardInterrupt) as exc:
handle_exception(app, args, exc, args.error)
return 2
finally:
if warnfp is not None:
# close the file descriptor for the warnings file opened by Sphinx
warnfp.close()
def _bug_report_info() -> int:

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from sphinx.util.console import _strip_escape_sequences
@ -6,7 +8,7 @@ if TYPE_CHECKING:
from typing import Protocol
class SupportsWrite(Protocol):
def write(self, text: str, /) -> None:
def write(self, text: str, /) -> int | None:
...

View File

@ -2,11 +2,14 @@
import os
import shutil
from contextlib import contextmanager
from pathlib import Path
from unittest import mock
import pytest
from docutils import nodes
from sphinx.cmd.build import build_main
from sphinx.errors import SphinxError
@ -133,3 +136,29 @@ def test_image_glob(app, status, warning):
assert doctree[0][3][0]['candidates'] == {'application/pdf': 'subdir/svgimg.pdf',
'image/svg+xml': 'subdir/svgimg.svg'}
assert doctree[0][3][0]['uri'] == 'subdir/svgimg.*'
@contextmanager
def force_colors():
forcecolor = os.environ.get('FORCE_COLOR', None)
try:
os.environ['FORCE_COLOR'] = '1'
yield
finally:
if forcecolor is None:
os.environ.pop('FORCE_COLOR', None)
else:
os.environ['FORCE_COLOR'] = forcecolor
def test_log_no_ansi_colors(tmp_path):
with force_colors():
wfile = tmp_path / 'warnings.txt'
srcdir = Path(__file__).parent / 'roots/test-nitpicky-warnings'
argv = list(map(str, ['-b', 'html', srcdir, tmp_path, '-n', '-w', wfile]))
retcode = build_main(argv)
assert retcode == 0
content = wfile.read_text(encoding='utf8')
assert '\x1b[91m' not in content