Skip to content

Commit

Permalink
Replace colorize() with functions from _cli.util.colour (#13259)
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner authored Jan 21, 2025
1 parent df962a4 commit 7154672
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 49 deletions.
34 changes: 26 additions & 8 deletions sphinx/_cli/util/colour.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@

from __future__ import annotations

import os
import sys
from collections.abc import Callable # NoQA: TC003
from os import environ as _environ

if False:
from collections.abc import Callable

if sys.platform == 'win32':
import colorama

colorama.just_fix_windows_console()
del colorama


_COLOURING_DISABLED = False


def terminal_supports_colour() -> bool:
"""Return True if coloured terminal output is supported."""
if 'NO_COLOUR' in os.environ or 'NO_COLOR' in os.environ:
if 'NO_COLOUR' in _environ or 'NO_COLOR' in _environ:
return False
if sys.platform == 'win32':
colorama.just_fix_windows_console()
return True
if 'FORCE_COLOUR' in os.environ or 'FORCE_COLOR' in os.environ:
if 'FORCE_COLOUR' in _environ or 'FORCE_COLOR' in _environ:
return True
if os.environ.get('CI', '') in {'true', '1'}:
if _environ.get('CI', '').lower() in {'true', '1'}:
return True

try:
Expand All @@ -34,7 +38,7 @@ def terminal_supports_colour() -> bool:
return False

# Do not colour output if on a dumb terminal
return os.environ.get('TERM', 'unknown').lower() not in {'dumb', 'unknown'}
return _environ.get('TERM', 'unknown').lower() not in {'dumb', 'unknown'}


def disable_colour() -> None:
Expand All @@ -50,7 +54,21 @@ def enable_colour() -> None:
def colourise(colour_name: str, text: str, /) -> str:
if _COLOURING_DISABLED:
return text
return globals()[colour_name](text)
if colour_name.startswith('_') or colour_name in {
'annotations',
'sys',
'terminal_supports_colour',
'disable_colour',
'enable_colour',
'colourise',
}:
msg = f'Invalid colour name: {colour_name!r}'
raise ValueError(msg)
try:
return globals()[colour_name](text)
except KeyError:
msg = f'Invalid colour name: {colour_name!r}'
raise ValueError(msg) from None


def _create_colour_func(escape_code: str, /) -> Callable[[str], str]:
Expand Down
56 changes: 31 additions & 25 deletions sphinx/cmd/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,44 @@
import time
from typing import TYPE_CHECKING

# try to import readline, unix specific enhancement
try:
import readline

if TYPE_CHECKING and sys.platform == 'win32': # always false, for type checking
raise ImportError # NoQA: TRY301
READLINE_AVAILABLE = True
if readline.__doc__ and 'libedit' in readline.__doc__:
readline.parse_and_bind('bind ^I rl_complete')
USE_LIBEDIT = True
else:
readline.parse_and_bind('tab: complete')
USE_LIBEDIT = False
except ImportError:
READLINE_AVAILABLE = False
USE_LIBEDIT = False

from docutils.utils import column_width

import sphinx.locale
from sphinx import __display_version__, package_dir
from sphinx._cli.util.colour import (
_create_input_mode_colour_func,
bold,
disable_colour,
red,
terminal_supports_colour,
)
from sphinx.locale import __
from sphinx.util.console import colorize
from sphinx.util.osutil import ensuredir
from sphinx.util.template import SphinxRenderer

if TYPE_CHECKING:
from collections.abc import Callable, Sequence
from typing import Any

# try to import readline, unix specific enhancement
try:
import readline

if TYPE_CHECKING and sys.platform == 'win32':
# MyPy doesn't realise that this raises a ModuleNotFoundError
# on Windows, and complains that 'parse_and_bind' is not defined.
# This condition is always False at runtime, but tricks type checkers.
raise ImportError # NoQA: TRY301
except ImportError:
READLINE_AVAILABLE = USE_LIBEDIT = False
else:
READLINE_AVAILABLE = True
USE_LIBEDIT = 'libedit' in getattr(readline, '__doc__', '')
if USE_LIBEDIT:
readline.parse_and_bind('bind ^I rl_complete')
else:
readline.parse_and_bind('tab: complete')

EXTENSIONS = {
'autodoc': __('automatically insert docstrings from modules'),
'doctest': __('automatically test code snippets in doctest blocks'),
Expand Down Expand Up @@ -73,10 +75,17 @@
PROMPT_PREFIX = '> '

if sys.platform == 'win32':
# On Windows, show questions as bold because of color scheme of PowerShell (refs: #5294).
COLOR_QUESTION = 'bold'
# On Windows, show questions as bold because of PowerShell's colour scheme
# (xref: https://github.com/sphinx-doc/sphinx/issues/5294).
from sphinx._cli.util.colour import bold as _question_colour
else:
COLOR_QUESTION = 'purple'
from sphinx._cli.util.colour import purple as _question_colour

if READLINE_AVAILABLE:
# Use an input-mode colour function if readline is available
if escape_code := getattr(_question_colour, '__escape_code', ''):
_question_colour = _create_input_mode_colour_func(escape_code)
del escape_code


# function to get input from terminal -- overridden by the test suite
Expand Down Expand Up @@ -158,11 +167,8 @@ def do_prompt(
# sequence (see #5335). To avoid the problem, all prompts are not colored
# on libedit.
pass
elif READLINE_AVAILABLE:
# pass input_mode=True if readline available
prompt = colorize(COLOR_QUESTION, prompt, input_mode=True)
else:
prompt = colorize(COLOR_QUESTION, prompt, input_mode=False)
prompt = _question_colour(prompt)
x = term_input(prompt).strip()
if default and not x:
x = default
Expand Down
30 changes: 14 additions & 16 deletions sphinx/util/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from docutils import nodes
from docutils.utils import get_source_line

from sphinx._cli.util.colour import colourise
from sphinx.errors import SphinxWarning
from sphinx.util.console import colorize

if TYPE_CHECKING:
from collections.abc import Iterator, Sequence, Set
Expand Down Expand Up @@ -49,14 +49,11 @@
},
)

COLOR_MAP: defaultdict[int, str] = defaultdict(
lambda: 'blue',
{
logging.ERROR: 'darkred',
logging.WARNING: 'red',
logging.DEBUG: 'darkgray',
},
)
COLOR_MAP: dict[int, str] = {
logging.ERROR: 'darkred',
logging.WARNING: 'red',
logging.DEBUG: 'darkgray',
}


def getLogger(name: str) -> SphinxLoggerAdapter:
Expand Down Expand Up @@ -566,13 +563,14 @@ def get_node_location(node: Node) -> str | None:
class ColorizeFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
message = super().format(record)
color = getattr(record, 'color', None)
if color is None:
color = COLOR_MAP.get(record.levelno)

if color:
return colorize(color, message)
else:
colour_name = getattr(record, 'color', '')
if not colour_name:
colour_name = COLOR_MAP.get(record.levelno, '')
if not colour_name:
return message
try:
return colourise(colour_name, message)
except ValueError:
return message


Expand Down

0 comments on commit 7154672

Please sign in to comment.