Use `sphinx._cli.util.colour in sphinx.util.console` (#13241)

This commit is contained in:
Adam Turner
2025-01-16 16:57:28 +00:00
committed by GitHub
parent dd7a00dfb5
commit a56fdad70b
36 changed files with 149 additions and 272 deletions

View File

View File

@@ -4,45 +4,38 @@ import itertools
import operator
from typing import TYPE_CHECKING
import pytest
from sphinx.util.console import blue, reset, strip_colors, strip_escape_sequences
from sphinx._cli.util.colour import blue, reset
from sphinx._cli.util.errors import strip_escape_sequences
if TYPE_CHECKING:
from collections.abc import Callable, Sequence
from collections.abc import Sequence
from typing import Final
CURSOR_UP: Final[str] = '\x1b[2A' # ignored ANSI code
ERASE_LINE: Final[str] = '\x1b[2K' # supported ANSI code
TEXT: Final[str] = '\x07 Hello world!'
TEXT: Final[str] = '\x07 ß Hello world!'
@pytest.mark.parametrize(
('strip_function', 'ansi_base_blocks', 'text_base_blocks'),
[
(
strip_colors,
# double ERASE_LINE so that the tested strings may have 2 of them
[TEXT, blue(TEXT), reset(TEXT), ERASE_LINE, ERASE_LINE, CURSOR_UP],
# :func:`strip_colors` removes color codes but keeps ERASE_LINE and CURSOR_UP
[TEXT, TEXT, TEXT, ERASE_LINE, ERASE_LINE, CURSOR_UP],
),
(
strip_escape_sequences,
# double ERASE_LINE so that the tested strings may have 2 of them
[TEXT, blue(TEXT), reset(TEXT), ERASE_LINE, ERASE_LINE, CURSOR_UP],
# :func:`strip_escape_sequences` strips ANSI codes known by Sphinx
[TEXT, TEXT, TEXT, '', '', CURSOR_UP],
),
],
ids=[strip_colors.__name__, strip_escape_sequences.__name__],
)
def test_strip_ansi(
strip_function: Callable[[str], str],
ansi_base_blocks: Sequence[str],
text_base_blocks: Sequence[str],
) -> None:
assert callable(strip_function)
def test_strip_escape_sequences() -> None:
# double ERASE_LINE so that the tested strings may have 2 of them
ansi_base_blocks = [
TEXT,
blue(TEXT),
reset(TEXT),
ERASE_LINE,
ERASE_LINE,
CURSOR_UP,
]
# :func:`strip_escape_sequences` strips ANSI codes known by Sphinx
text_base_blocks = [
TEXT,
TEXT,
TEXT,
'',
'',
CURSOR_UP,
]
assert len(text_base_blocks) == len(ansi_base_blocks)
N = len(ansi_base_blocks)
@@ -67,7 +60,7 @@ def test_strip_ansi(
ansi_string = glue.join(ansi_strings)
text_string = glue.join(text_strings)
assert strip_function(ansi_string) == text_string
assert strip_escape_sequences(ansi_string) == text_string
def test_strip_ansi_short_forms():
@@ -76,7 +69,7 @@ def test_strip_ansi_short_forms():
# some messages use '\x1b[0m' instead of ``reset(s)``, so we
# test whether this alternative form is supported or not.
for strip_function in strip_colors, strip_escape_sequences:
for strip_function in strip_escape_sequences, strip_escape_sequences:
# \x1b[m and \x1b[0m are equivalent to \x1b[00m
assert strip_function('\x1b[m') == ''
assert strip_function('\x1b[0m') == ''

View File

@@ -13,10 +13,10 @@ import pytest
from docutils import nodes
import sphinx.application
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.errors import ExtensionError
from sphinx.testing.util import SphinxTestApp
from sphinx.util import logging
from sphinx.util.console import strip_colors
if TYPE_CHECKING:
import os
@@ -84,14 +84,14 @@ def test_emit_with_nonascii_name_node(app):
@pytest.mark.sphinx('html', testroot='root')
def test_extensions(app):
app.setup_extension('shutil')
warning = strip_colors(app.warning.getvalue())
warning = strip_escape_sequences(app.warning.getvalue())
assert "extension 'shutil' has no setup() function" in warning
@pytest.mark.sphinx('html', testroot='root')
def test_extension_in_blacklist(app):
app.setup_extension('sphinxjp.themecore')
msg = strip_colors(app.warning.getvalue())
msg = strip_escape_sequences(app.warning.getvalue())
assert msg.startswith("WARNING: the extension 'sphinxjp.themecore' was")

View File

@@ -9,9 +9,9 @@ from typing import TYPE_CHECKING
import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.util.console import strip_colors
from sphinx.util.inventory import InventoryFile
from tests.test_builders.xpath_data import FIGURE_CAPTION
@@ -512,7 +512,7 @@ def test_validate_html_extra_path(app):
validate_html_extra_path(app, app.config)
assert app.config.html_extra_path == ['_static']
warnings = strip_colors(app.warning.getvalue()).splitlines()
warnings = strip_escape_sequences(app.warning.getvalue()).splitlines()
assert "html_extra_path entry '/path/to/not_found' does not exist" in warnings[0]
assert warnings[1].endswith(' is placed inside outdir')
assert warnings[2].endswith(' does not exist')
@@ -536,7 +536,7 @@ def test_validate_html_static_path(app):
validate_html_static_path(app, app.config)
assert app.config.html_static_path == ['_static']
warnings = strip_colors(app.warning.getvalue()).splitlines()
warnings = strip_escape_sequences(app.warning.getvalue()).splitlines()
assert "html_static_path entry '/path/to/not_found' does not exist" in warnings[0]
assert warnings[1].endswith(' is placed inside outdir')
assert warnings[2].endswith(' does not exist')
@@ -598,7 +598,7 @@ def test_html_remove_sources_before_write_gh_issue_10786(app):
app.build()
assert not target.exists()
ws = strip_colors(app.warning.getvalue()).splitlines()
ws = strip_escape_sequences(app.warning.getvalue()).splitlines()
assert len(ws) >= 1
file = os.fsdecode(target)

View File

@@ -19,6 +19,7 @@ import pytest
from urllib3.poolmanager import PoolManager
import sphinx.util.http_date
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.builders.linkcheck import (
CheckRequest,
Hyperlink,
@@ -28,7 +29,6 @@ from sphinx.builders.linkcheck import (
)
from sphinx.util import requests
from sphinx.util._pathlib import _StrPath
from sphinx.util.console import strip_colors
from tests.utils import CERT_FILE, serve_application
@@ -775,7 +775,7 @@ def test_linkcheck_allowed_redirects(app: SphinxTestApp) -> None:
assert (
f'index.rst:3: WARNING: redirect http://{address}/path2 - with Found to '
f'http://{address}/?redirected=1\n'
) in strip_colors(app.warning.getvalue())
) in strip_escape_sequences(app.warning.getvalue())
assert len(app.warning.getvalue().splitlines()) == 1
@@ -1061,7 +1061,7 @@ def test_too_many_requests_retry_after_int_delay(app, capsys):
'info': '',
}
rate_limit_log = f'-rate limited- http://{address}/ | sleeping...\n'
assert rate_limit_log in strip_colors(app.status.getvalue())
assert rate_limit_log in strip_escape_sequences(app.status.getvalue())
_stdout, stderr = capsys.readouterr()
assert stderr == textwrap.dedent(
"""\

View File

@@ -7,8 +7,8 @@ import traceback
import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.errors import SphinxError
from sphinx.util.console import strip_colors
ENV_WARNINGS = """\
{root}/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \
@@ -55,7 +55,7 @@ TEXINFO_WARNINGS = (
def _check_warnings(expected_warnings: str, warning: str) -> None:
warnings = strip_colors(re.sub(re.escape(os.sep) + '{1,2}', '/', warning))
warnings = strip_escape_sequences(re.sub(re.escape(os.sep) + '{1,2}', '/', warning))
assert re.match(f'{expected_warnings}$', warnings), (
"Warnings don't match:\n"
f'--- Expected (regex):\n{expected_warnings}\n'
@@ -127,7 +127,7 @@ def setup(app):
tmp_path.joinpath('index.rst').write_text('Test\n====\n', encoding='utf-8')
app = make_app(srcdir=tmp_path)
app.build()
assert strip_colors(app.warning.getvalue()).strip() == (
assert strip_escape_sequences(app.warning.getvalue()).strip() == (
"WARNING: cannot cache unpickable configuration value: 'my_config' "
'(because it contains a function, class, or module object) [config.cache]'
)

View File

@@ -7,6 +7,7 @@ from pathlib import Path
import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder
from sphinx.config import Config
@@ -17,7 +18,6 @@ from sphinx.environment import (
CONFIG_OK,
_differing_config_keys,
)
from sphinx.util.console import strip_colors
@pytest.mark.sphinx('dummy', testroot='basic')
@@ -28,7 +28,7 @@ def test_config_status(make_app, app_params):
app1 = make_app(*args, freshenv=True, **kwargs)
assert app1.env.config_status == CONFIG_NEW
app1.build()
output = strip_colors(app1.status.getvalue())
output = strip_escape_sequences(app1.status.getvalue())
# assert 'The configuration has changed' not in output
assert '[new config] 1 added' in output
@@ -36,7 +36,7 @@ def test_config_status(make_app, app_params):
app2 = make_app(*args, **kwargs)
assert app2.env.config_status == CONFIG_OK
app2.build()
output = strip_colors(app2.status.getvalue())
output = strip_escape_sequences(app2.status.getvalue())
assert 'The configuration has changed' not in output
assert '0 added, 0 changed, 0 removed' in output
@@ -49,7 +49,7 @@ def test_config_status(make_app, app_params):
assert app3.env.config_status == CONFIG_CHANGED
app3.build()
shutil.move(other_fname, fname)
output = strip_colors(app3.status.getvalue())
output = strip_escape_sequences(app3.status.getvalue())
assert 'The configuration has changed' in output
assert "[config changed ('master_doc')] 1 added," in output
@@ -60,7 +60,7 @@ def test_config_status(make_app, app_params):
assert app4.env.config_status == CONFIG_EXTENSIONS_CHANGED
app4.build()
want_str = "[extensions changed ('sphinx.ext.autodoc')] 1 added"
output = strip_colors(app4.status.getvalue())
output = strip_escape_sequences(app4.status.getvalue())
assert 'The configuration has changed' not in output
assert want_str in output

View File

@@ -11,6 +11,7 @@ import pytest
from docutils import nodes
from sphinx import addnodes
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.builders.html import INVENTORY_FILENAME
from sphinx.config import Config
from sphinx.errors import ConfigError
@@ -27,7 +28,6 @@ from sphinx.ext.intersphinx._load import (
)
from sphinx.ext.intersphinx._resolve import missing_reference
from sphinx.ext.intersphinx._shared import _IntersphinxProject
from sphinx.util.console import strip_colors
from tests.test_util.intersphinx_data import (
INVENTORY_V2,
@@ -530,7 +530,7 @@ def test_validate_intersphinx_mapping_warnings(app):
match=r'^Invalid `intersphinx_mapping` configuration \(16 errors\).$',
):
validate_intersphinx_mapping(app, app.config)
warnings = strip_colors(app.warning.getvalue()).splitlines()
warnings = strip_escape_sequences(app.warning.getvalue()).splitlines()
assert len(warnings) == len(bad_intersphinx_mapping) - 3
assert warnings == [
"ERROR: Invalid intersphinx project identifier `''` in intersphinx_mapping. Project identifiers must be non-empty strings.",
@@ -705,7 +705,7 @@ def test_intersphinx_role(app):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
warnings = strip_colors(app.warning.getvalue()).splitlines()
warnings = strip_escape_sequences(app.warning.getvalue()).splitlines()
index_path = app.srcdir / 'index.rst'
assert warnings == [
f"{index_path}:21: WARNING: role for external cross-reference not found in domain 'py': 'nope' [intersphinx.external]",

View File

@@ -18,8 +18,8 @@ from babel.messages.catalog import Catalog
from docutils import nodes
from sphinx import locale
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.testing.util import assert_node, etree_parse
from sphinx.util.console import strip_colors
from sphinx.util.nodes import NodeMatcher
if TYPE_CHECKING:
@@ -1893,7 +1893,7 @@ def test_image_glob_intl_using_figure_language_filename(app):
def getwarning(warnings: StringIO) -> str:
return strip_colors(warnings.getvalue().replace(os.sep, '/'))
return strip_escape_sequences(warnings.getvalue().replace(os.sep, '/'))
@pytest.mark.sphinx(

View File

@@ -8,9 +8,9 @@ from typing import TYPE_CHECKING
import pytest
from sphinx._cli.util.colour import disable_colour, enable_colour
from sphinx.cmd import quickstart as qs
from sphinx.testing.util import SphinxTestApp
from sphinx.util.console import coloron, nocolor
if TYPE_CHECKING:
from collections.abc import Callable
@@ -21,7 +21,7 @@ warnfile = StringIO()
def setup_module():
nocolor()
disable_colour()
def mock_input(
@@ -50,7 +50,7 @@ real_input: Callable[[str], str] = input
def teardown_module():
qs.term_input = real_input
coloron()
enable_colour()
def test_do_prompt():

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from sphinx.util.console import strip_colors
from sphinx._cli.util.errors import strip_escape_sequences
if TYPE_CHECKING:
from pathlib import Path
@@ -35,7 +35,7 @@ Title
)
app = make_app(srcdir=tmp_path)
app.build()
warnings = strip_colors(app.warning.getvalue()).lstrip()
warnings = strip_escape_sequences(app.warning.getvalue()).lstrip()
warnings = warnings.replace(str(tmp_path / 'index.rst'), 'source/index.rst')
assert (
warnings

View File

@@ -5,13 +5,13 @@ from __future__ import annotations
import pytest
import sphinx.util
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.deprecation import RemovedInSphinx10Warning, RemovedInSphinx90Warning
from sphinx.errors import ExtensionError
from sphinx.util._files import DownloadFiles, FilenameUniqDict
from sphinx.util._importer import import_object
from sphinx.util._lines import parse_line_num_spec
from sphinx.util._uri import encode_uri, is_url
from sphinx.util.console import strip_colors
from sphinx.util.index_entries import _split_into, split_index_msg
from sphinx.util.matching import patfilter
from sphinx.util.nodes import (
@@ -78,7 +78,7 @@ def test_exported_attributes():
assert sphinx.util.isurl is is_url
assert sphinx.util.parselinenos is parse_line_num_spec
assert sphinx.util.patfilter is patfilter
assert sphinx.util.strip_colors is strip_colors
assert sphinx.util.strip_escape_sequences is strip_escape_sequences
assert sphinx.util.caption_ref_re is caption_ref_re
assert sphinx.util.explicit_title_re is explicit_title_re
assert sphinx.util.nested_parse_with_titles is nested_parse_with_titles

View File

@@ -4,8 +4,8 @@ from __future__ import annotations
import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.util import logging
from sphinx.util.console import strip_colors
from sphinx.util.display import (
SkipProgressMessage,
display_chunk,
@@ -30,7 +30,7 @@ def test_status_iterator_length_0(app):
app.status.seek(0)
app.status.truncate(0)
yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... '))
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing ... hello sphinx world \n' in output
assert yields == ['hello', 'sphinx', 'world']
@@ -47,7 +47,7 @@ def test_status_iterator_verbosity_0(app, monkeypatch):
['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=0
)
assert list(yields) == ['hello', 'sphinx', 'world']
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing ... [ 33%] hello\r' in output
assert 'testing ... [ 67%] sphinx\r' in output
assert 'testing ... [100%] world\r\n' in output
@@ -65,7 +65,7 @@ def test_status_iterator_verbosity_1(app, monkeypatch):
['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=1
)
assert list(yields) == ['hello', 'sphinx', 'world']
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing ... [ 33%] hello\n' in output
assert 'testing ... [ 67%] sphinx\n' in output
assert 'testing ... [100%] world\n\n' in output
@@ -80,14 +80,14 @@ def test_progress_message(app):
with progress_message('testing'):
logger.info('blah ', nonl=True)
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing... blah done\n' in output
# skipping case
with progress_message('testing'):
raise SkipProgressMessage('Reason: %s', 'error') # NoQA: EM101,TRY003
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing... skipped\nReason: error\n' in output
# error case
@@ -97,7 +97,7 @@ def test_progress_message(app):
except Exception:
pass
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing... failed\n' in output
# decorator
@@ -106,5 +106,5 @@ def test_progress_message(app):
logger.info('in func ', nonl=True)
func()
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert 'testing... in func done\n' in output

View File

@@ -7,8 +7,8 @@ from unittest import mock
import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.console import strip_colors
from sphinx.util.fileutil import _template_basename, copy_asset, copy_asset_file
@@ -127,7 +127,7 @@ def test_copy_asset_template(app):
app.build(force_all=True)
expected_msg = r'^Writing evaluated template result to [^\n]*\bAPI.html$'
output = strip_colors(app.status.getvalue())
output = strip_escape_sequences(app.status.getvalue())
assert re.findall(expected_msg, output, flags=re.MULTILINE)
@@ -136,7 +136,7 @@ def test_copy_asset_overwrite(app):
app.build()
src = app.srcdir / 'myext_static' / 'custom-styles.css'
dst = app.outdir / '_static' / 'custom-styles.css'
assert strip_colors(app.warning.getvalue()) == (
assert strip_escape_sequences(app.warning.getvalue()) == (
f'WARNING: Aborted attempted copy from {src} to {dst} '
'(the destination path has existing data). '
'[misc.copy_overwrite]\n'

View File

@@ -9,8 +9,9 @@ from pathlib import Path
import pytest
from docutils import nodes
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.util import logging
from sphinx.util.console import colorize, strip_colors
from sphinx.util.console import colorize
from sphinx.util.logging import is_suppressed_warning, prefixed_warnings
from sphinx.util.parallel import ParallelTasks
@@ -115,7 +116,7 @@ def test_once_warning_log(app):
logger.warning('message: %d', 1, once=True)
logger.warning('message: %d', 2, once=True)
warnings = strip_colors(app.warning.getvalue())
warnings = strip_escape_sequences(app.warning.getvalue())
assert 'WARNING: message: 1\nWARNING: message: 2\n' in warnings
@@ -271,7 +272,7 @@ def test_pending_warnings(app):
assert 'WARNING: message3' not in app.warning.getvalue()
# actually logged as ordered
warnings = strip_colors(app.warning.getvalue())
warnings = strip_escape_sequences(app.warning.getvalue())
assert 'WARNING: message2\nWARNING: message3' in warnings
@@ -381,7 +382,7 @@ def test_show_warning_types(app):
logger.warning('message3', type='test')
logger.warning('message4', type='test', subtype='logging')
warnings = strip_colors(app.warning.getvalue()).splitlines()
warnings = strip_escape_sequences(app.warning.getvalue()).splitlines()
assert warnings == [
'WARNING: message2',