mirror of
https://github.com/docker/compose.git
synced 2025-07-22 21:24:38 +02:00
Improve control over ANSI output (#6858)
* Move global console_handler into function scope Signed-off-by: Mike Seplowitz <mseplowitz@bloomberg.net> * Improve control over ANSI output - Disabled parallel logger ANSI output if not attached to a tty. The console handler and progress stream already checked whether the output stream is a tty, but ParallelStreamWriter did not. - Added --ansi=(never|always|auto) option to allow clearer control over ANSI output. Since --no-ansi is the same as --ansi=never, --no-ansi is now deprecated. Signed-off-by: Mike Seplowitz <mseplowitz@bloomberg.net>
This commit is contained in:
parent
e1fb1e9a3a
commit
b6ddddc31a
@ -1,3 +1,6 @@
|
|||||||
|
import enum
|
||||||
|
import os
|
||||||
|
|
||||||
from ..const import IS_WINDOWS_PLATFORM
|
from ..const import IS_WINDOWS_PLATFORM
|
||||||
|
|
||||||
NAMES = [
|
NAMES = [
|
||||||
@ -12,6 +15,21 @@ NAMES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@enum.unique
|
||||||
|
class AnsiMode(enum.Enum):
|
||||||
|
"""Enumeration for when to output ANSI colors."""
|
||||||
|
NEVER = "never"
|
||||||
|
ALWAYS = "always"
|
||||||
|
AUTO = "auto"
|
||||||
|
|
||||||
|
def use_ansi_codes(self, stream):
|
||||||
|
if self is AnsiMode.ALWAYS:
|
||||||
|
return True
|
||||||
|
if self is AnsiMode.NEVER or os.environ.get('CLICOLOR') == '0':
|
||||||
|
return False
|
||||||
|
return stream.isatty()
|
||||||
|
|
||||||
|
|
||||||
def get_pairs():
|
def get_pairs():
|
||||||
for i, name in enumerate(NAMES):
|
for i, name in enumerate(NAMES):
|
||||||
yield (name, str(30 + i))
|
yield (name, str(30 + i))
|
||||||
|
@ -2,7 +2,6 @@ import contextlib
|
|||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import pipes
|
import pipes
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -27,6 +26,7 @@ from ..config.types import VolumeSpec
|
|||||||
from ..const import IS_WINDOWS_PLATFORM
|
from ..const import IS_WINDOWS_PLATFORM
|
||||||
from ..errors import StreamParseError
|
from ..errors import StreamParseError
|
||||||
from ..metrics.decorator import metrics
|
from ..metrics.decorator import metrics
|
||||||
|
from ..parallel import ParallelStreamWriter
|
||||||
from ..progress_stream import StreamOutputError
|
from ..progress_stream import StreamOutputError
|
||||||
from ..project import get_image_digests
|
from ..project import get_image_digests
|
||||||
from ..project import MissingDigests
|
from ..project import MissingDigests
|
||||||
@ -40,6 +40,7 @@ from ..service import ImageType
|
|||||||
from ..service import NeedsBuildError
|
from ..service import NeedsBuildError
|
||||||
from ..service import OperationFailedError
|
from ..service import OperationFailedError
|
||||||
from ..utils import filter_attached_for_up
|
from ..utils import filter_attached_for_up
|
||||||
|
from .colors import AnsiMode
|
||||||
from .command import get_config_from_options
|
from .command import get_config_from_options
|
||||||
from .command import get_project_dir
|
from .command import get_project_dir
|
||||||
from .command import project_from_options
|
from .command import project_from_options
|
||||||
@ -62,7 +63,6 @@ if not IS_WINDOWS_PLATFORM:
|
|||||||
from dockerpty.pty import PseudoTerminal, RunOperation, ExecOperation
|
from dockerpty.pty import PseudoTerminal, RunOperation, ExecOperation
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
console_handler = logging.StreamHandler(sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def main(): # noqa: C901
|
def main(): # noqa: C901
|
||||||
@ -139,18 +139,38 @@ def exit_with_metrics(command, log_msg=None, status=Status.SUCCESS, exit_code=1)
|
|||||||
|
|
||||||
|
|
||||||
def dispatch():
|
def dispatch():
|
||||||
setup_logging()
|
console_stream = sys.stderr
|
||||||
|
console_handler = logging.StreamHandler(console_stream)
|
||||||
|
setup_logging(console_handler)
|
||||||
dispatcher = DocoptDispatcher(
|
dispatcher = DocoptDispatcher(
|
||||||
TopLevelCommand,
|
TopLevelCommand,
|
||||||
{'options_first': True, 'version': get_version_info('compose')})
|
{'options_first': True, 'version': get_version_info('compose')})
|
||||||
|
|
||||||
options, handler, command_options = dispatcher.parse(sys.argv[1:])
|
options, handler, command_options = dispatcher.parse(sys.argv[1:])
|
||||||
|
|
||||||
|
ansi_mode = AnsiMode.AUTO
|
||||||
|
try:
|
||||||
|
if options.get("--ansi"):
|
||||||
|
ansi_mode = AnsiMode(options.get("--ansi"))
|
||||||
|
except ValueError:
|
||||||
|
raise UserError(
|
||||||
|
'Invalid value for --ansi: {}. Expected one of {}.'.format(
|
||||||
|
options.get("--ansi"),
|
||||||
|
', '.join(m.value for m in AnsiMode)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if options.get("--no-ansi"):
|
||||||
|
if options.get("--ansi"):
|
||||||
|
raise UserError("--no-ansi and --ansi cannot be combined.")
|
||||||
|
log.warning('--no-ansi option is deprecated and will be removed in future versions.')
|
||||||
|
ansi_mode = AnsiMode.NEVER
|
||||||
|
|
||||||
setup_console_handler(console_handler,
|
setup_console_handler(console_handler,
|
||||||
options.get('--verbose'),
|
options.get('--verbose'),
|
||||||
set_no_color_if_clicolor(options.get('--no-ansi')),
|
ansi_mode.use_ansi_codes(console_handler.stream),
|
||||||
options.get("--log-level"))
|
options.get("--log-level"))
|
||||||
setup_parallel_logger(set_no_color_if_clicolor(options.get('--no-ansi')))
|
setup_parallel_logger(ansi_mode)
|
||||||
if options.get('--no-ansi'):
|
if ansi_mode is AnsiMode.NEVER:
|
||||||
command_options['--no-color'] = True
|
command_options['--no-color'] = True
|
||||||
return functools.partial(perform_command, options, handler, command_options)
|
return functools.partial(perform_command, options, handler, command_options)
|
||||||
|
|
||||||
@ -172,7 +192,7 @@ def perform_command(options, handler, command_options):
|
|||||||
handler(command, command_options)
|
handler(command, command_options)
|
||||||
|
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging(console_handler):
|
||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
root_logger.addHandler(console_handler)
|
root_logger.addHandler(console_handler)
|
||||||
root_logger.setLevel(logging.DEBUG)
|
root_logger.setLevel(logging.DEBUG)
|
||||||
@ -183,14 +203,12 @@ def setup_logging():
|
|||||||
logging.getLogger("docker").propagate = False
|
logging.getLogger("docker").propagate = False
|
||||||
|
|
||||||
|
|
||||||
def setup_parallel_logger(noansi):
|
def setup_parallel_logger(ansi_mode):
|
||||||
if noansi:
|
ParallelStreamWriter.set_default_ansi_mode(ansi_mode)
|
||||||
import compose.parallel
|
|
||||||
compose.parallel.ParallelStreamWriter.set_noansi()
|
|
||||||
|
|
||||||
|
|
||||||
def setup_console_handler(handler, verbose, noansi=False, level=None):
|
def setup_console_handler(handler, verbose, use_console_formatter=True, level=None):
|
||||||
if handler.stream.isatty() and noansi is False:
|
if use_console_formatter:
|
||||||
format_class = ConsoleWarningFormatter
|
format_class = ConsoleWarningFormatter
|
||||||
else:
|
else:
|
||||||
format_class = logging.Formatter
|
format_class = logging.Formatter
|
||||||
@ -242,7 +260,8 @@ class TopLevelCommand:
|
|||||||
-c, --context NAME Specify a context name
|
-c, --context NAME Specify a context name
|
||||||
--verbose Show more output
|
--verbose Show more output
|
||||||
--log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
--log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
--no-ansi Do not print ANSI control characters
|
--ansi (never|always|auto) Control when to print ANSI control characters
|
||||||
|
--no-ansi Do not print ANSI control characters (DEPRECATED)
|
||||||
-v, --version Print version and exit
|
-v, --version Print version and exit
|
||||||
-H, --host HOST Daemon socket to connect to
|
-H, --host HOST Daemon socket to connect to
|
||||||
|
|
||||||
@ -691,7 +710,7 @@ class TopLevelCommand:
|
|||||||
log_printer_from_project(
|
log_printer_from_project(
|
||||||
self.project,
|
self.project,
|
||||||
containers,
|
containers,
|
||||||
set_no_color_if_clicolor(options['--no-color']),
|
options['--no-color'],
|
||||||
log_args,
|
log_args,
|
||||||
event_stream=self.project.events(service_names=options['SERVICE']),
|
event_stream=self.project.events(service_names=options['SERVICE']),
|
||||||
keep_prefix=not options['--no-log-prefix']).run()
|
keep_prefix=not options['--no-log-prefix']).run()
|
||||||
@ -1167,7 +1186,7 @@ class TopLevelCommand:
|
|||||||
log_printer = log_printer_from_project(
|
log_printer = log_printer_from_project(
|
||||||
self.project,
|
self.project,
|
||||||
attached_containers,
|
attached_containers,
|
||||||
set_no_color_if_clicolor(options['--no-color']),
|
options['--no-color'],
|
||||||
{'follow': True},
|
{'follow': True},
|
||||||
cascade_stop,
|
cascade_stop,
|
||||||
event_stream=self.project.events(service_names=service_names),
|
event_stream=self.project.events(service_names=service_names),
|
||||||
@ -1651,7 +1670,3 @@ def warn_for_swarm_mode(client):
|
|||||||
"To deploy your application across the swarm, "
|
"To deploy your application across the swarm, "
|
||||||
"use `docker stack deploy`.\n"
|
"use `docker stack deploy`.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_no_color_if_clicolor(no_color_flag):
|
|
||||||
return no_color_flag or os.environ.get('CLICOLOR') == "0"
|
|
||||||
|
@ -11,6 +11,7 @@ from threading import Thread
|
|||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
from docker.errors import ImageNotFound
|
from docker.errors import ImageNotFound
|
||||||
|
|
||||||
|
from compose.cli.colors import AnsiMode
|
||||||
from compose.cli.colors import green
|
from compose.cli.colors import green
|
||||||
from compose.cli.colors import red
|
from compose.cli.colors import red
|
||||||
from compose.cli.signals import ShutdownException
|
from compose.cli.signals import ShutdownException
|
||||||
@ -83,10 +84,7 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None, fa
|
|||||||
objects = list(objects)
|
objects = list(objects)
|
||||||
stream = sys.stderr
|
stream = sys.stderr
|
||||||
|
|
||||||
if ParallelStreamWriter.instance:
|
writer = ParallelStreamWriter.get_or_assign_instance(ParallelStreamWriter(stream))
|
||||||
writer = ParallelStreamWriter.instance
|
|
||||||
else:
|
|
||||||
writer = ParallelStreamWriter(stream)
|
|
||||||
|
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
writer.add_object(msg, get_name(obj))
|
writer.add_object(msg, get_name(obj))
|
||||||
@ -259,19 +257,37 @@ class ParallelStreamWriter:
|
|||||||
to jump to the correct line, and write over the line.
|
to jump to the correct line, and write over the line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
noansi = False
|
default_ansi_mode = AnsiMode.AUTO
|
||||||
lock = Lock()
|
write_lock = Lock()
|
||||||
|
|
||||||
instance = None
|
instance = None
|
||||||
|
instance_lock = Lock()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_noansi(cls, value=True):
|
def get_instance(cls):
|
||||||
cls.noansi = value
|
return cls.instance
|
||||||
|
|
||||||
def __init__(self, stream):
|
@classmethod
|
||||||
|
def get_or_assign_instance(cls, writer):
|
||||||
|
cls.instance_lock.acquire()
|
||||||
|
try:
|
||||||
|
if cls.instance is None:
|
||||||
|
cls.instance = writer
|
||||||
|
return cls.instance
|
||||||
|
finally:
|
||||||
|
cls.instance_lock.release()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_default_ansi_mode(cls, ansi_mode):
|
||||||
|
cls.default_ansi_mode = ansi_mode
|
||||||
|
|
||||||
|
def __init__(self, stream, ansi_mode=None):
|
||||||
|
if ansi_mode is None:
|
||||||
|
ansi_mode = self.default_ansi_mode
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
self.use_ansi_codes = ansi_mode.use_ansi_codes(stream)
|
||||||
self.lines = []
|
self.lines = []
|
||||||
self.width = 0
|
self.width = 0
|
||||||
ParallelStreamWriter.instance = self
|
|
||||||
|
|
||||||
def add_object(self, msg, obj_index):
|
def add_object(self, msg, obj_index):
|
||||||
if msg is None:
|
if msg is None:
|
||||||
@ -285,7 +301,7 @@ class ParallelStreamWriter:
|
|||||||
return self._write_noansi(msg, obj_index, '')
|
return self._write_noansi(msg, obj_index, '')
|
||||||
|
|
||||||
def _write_ansi(self, msg, obj_index, status):
|
def _write_ansi(self, msg, obj_index, status):
|
||||||
self.lock.acquire()
|
self.write_lock.acquire()
|
||||||
position = self.lines.index(msg + obj_index)
|
position = self.lines.index(msg + obj_index)
|
||||||
diff = len(self.lines) - position
|
diff = len(self.lines) - position
|
||||||
# move up
|
# move up
|
||||||
@ -297,7 +313,7 @@ class ParallelStreamWriter:
|
|||||||
# move back down
|
# move back down
|
||||||
self.stream.write("%c[%dB" % (27, diff))
|
self.stream.write("%c[%dB" % (27, diff))
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
self.lock.release()
|
self.write_lock.release()
|
||||||
|
|
||||||
def _write_noansi(self, msg, obj_index, status):
|
def _write_noansi(self, msg, obj_index, status):
|
||||||
self.stream.write(
|
self.stream.write(
|
||||||
@ -310,17 +326,10 @@ class ParallelStreamWriter:
|
|||||||
def write(self, msg, obj_index, status, color_func):
|
def write(self, msg, obj_index, status, color_func):
|
||||||
if msg is None:
|
if msg is None:
|
||||||
return
|
return
|
||||||
if self.noansi:
|
if self.use_ansi_codes:
|
||||||
self._write_noansi(msg, obj_index, status)
|
|
||||||
else:
|
|
||||||
self._write_ansi(msg, obj_index, color_func(status))
|
self._write_ansi(msg, obj_index, color_func(status))
|
||||||
|
else:
|
||||||
|
self._write_noansi(msg, obj_index, status)
|
||||||
def get_stream_writer():
|
|
||||||
instance = ParallelStreamWriter.instance
|
|
||||||
if instance is None:
|
|
||||||
raise RuntimeError('ParallelStreamWriter has not yet been instantiated')
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
def parallel_operation(containers, operation, options, message):
|
def parallel_operation(containers, operation, options, message):
|
||||||
|
@ -789,7 +789,9 @@ class Project:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
writer = parallel.get_stream_writer()
|
writer = parallel.ParallelStreamWriter.get_instance()
|
||||||
|
if writer is None:
|
||||||
|
raise RuntimeError('ParallelStreamWriter has not yet been instantiated')
|
||||||
for event in strm:
|
for event in strm:
|
||||||
if 'status' not in event:
|
if 'status' not in event:
|
||||||
continue
|
continue
|
||||||
|
@ -164,6 +164,10 @@ _docker_compose_docker_compose() {
|
|||||||
_filedir "y?(a)ml"
|
_filedir "y?(a)ml"
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
--ansi)
|
||||||
|
COMPREPLY=( $( compgen -W "never always auto" -- "$cur" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
--log-level)
|
--log-level)
|
||||||
COMPREPLY=( $( compgen -W "debug info warning error critical" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "debug info warning error critical" -- "$cur" ) )
|
||||||
return
|
return
|
||||||
@ -616,6 +620,7 @@ _docker_compose() {
|
|||||||
|
|
||||||
# These options are require special treatment when searching the command.
|
# These options are require special treatment when searching the command.
|
||||||
local top_level_options_with_args="
|
local top_level_options_with_args="
|
||||||
|
--ansi
|
||||||
--log-level
|
--log-level
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -21,5 +21,7 @@ complete -c docker-compose -l tlscert -r -d 'Path to TLS certif
|
|||||||
complete -c docker-compose -l tlskey -r -d 'Path to TLS key file'
|
complete -c docker-compose -l tlskey -r -d 'Path to TLS key file'
|
||||||
complete -c docker-compose -l tlsverify -d 'Use TLS and verify the remote'
|
complete -c docker-compose -l tlsverify -d 'Use TLS and verify the remote'
|
||||||
complete -c docker-compose -l skip-hostname-check -d "Don't check the daemon's hostname against the name specified in the client certificate (for example if your docker host is an IP address)"
|
complete -c docker-compose -l skip-hostname-check -d "Don't check the daemon's hostname against the name specified in the client certificate (for example if your docker host is an IP address)"
|
||||||
|
complete -c docker-compose -l no-ansi -d 'Do not print ANSI control characters'
|
||||||
|
complete -c docker-compose -l ansi -a never always auto -d 'Control when to print ANSI control characters'
|
||||||
complete -c docker-compose -s h -l help -d 'Print usage'
|
complete -c docker-compose -s h -l help -d 'Print usage'
|
||||||
complete -c docker-compose -s v -l version -d 'Print version and exit'
|
complete -c docker-compose -s v -l version -d 'Print version and exit'
|
||||||
|
@ -342,6 +342,7 @@ _docker-compose() {
|
|||||||
'--verbose[Show more output]' \
|
'--verbose[Show more output]' \
|
||||||
'--log-level=[Set log level]:level:(DEBUG INFO WARNING ERROR CRITICAL)' \
|
'--log-level=[Set log level]:level:(DEBUG INFO WARNING ERROR CRITICAL)' \
|
||||||
'--no-ansi[Do not print ANSI control characters]' \
|
'--no-ansi[Do not print ANSI control characters]' \
|
||||||
|
'--ansi=[Control when to print ANSI control characters]:when:(never always auto)' \
|
||||||
'(-H --host)'{-H,--host}'[Daemon socket to connect to]:host:' \
|
'(-H --host)'{-H,--host}'[Daemon socket to connect to]:host:' \
|
||||||
'--tls[Use TLS; implied by --tlsverify]' \
|
'--tls[Use TLS; implied by --tlsverify]' \
|
||||||
'--tlscacert=[Trust certs signed only by this CA]:ca path:' \
|
'--tlscacert=[Trust certs signed only by this CA]:ca path:' \
|
||||||
|
56
tests/unit/cli/colors_test.py
Normal file
56
tests/unit/cli/colors_test.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from compose.cli.colors import AnsiMode
|
||||||
|
from tests import mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def tty_stream():
|
||||||
|
stream = mock.Mock()
|
||||||
|
stream.isatty.return_value = True
|
||||||
|
return stream
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def non_tty_stream():
|
||||||
|
stream = mock.Mock()
|
||||||
|
stream.isatty.return_value = False
|
||||||
|
return stream
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnsiModeTestCase:
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ)
|
||||||
|
def test_ansi_mode_never(self, tty_stream, non_tty_stream):
|
||||||
|
if "CLICOLOR" in os.environ:
|
||||||
|
del os.environ["CLICOLOR"]
|
||||||
|
assert not AnsiMode.NEVER.use_ansi_codes(tty_stream)
|
||||||
|
assert not AnsiMode.NEVER.use_ansi_codes(non_tty_stream)
|
||||||
|
|
||||||
|
os.environ["CLICOLOR"] = "0"
|
||||||
|
assert not AnsiMode.NEVER.use_ansi_codes(tty_stream)
|
||||||
|
assert not AnsiMode.NEVER.use_ansi_codes(non_tty_stream)
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ)
|
||||||
|
def test_ansi_mode_always(self, tty_stream, non_tty_stream):
|
||||||
|
if "CLICOLOR" in os.environ:
|
||||||
|
del os.environ["CLICOLOR"]
|
||||||
|
assert AnsiMode.ALWAYS.use_ansi_codes(tty_stream)
|
||||||
|
assert AnsiMode.ALWAYS.use_ansi_codes(non_tty_stream)
|
||||||
|
|
||||||
|
os.environ["CLICOLOR"] = "0"
|
||||||
|
assert AnsiMode.ALWAYS.use_ansi_codes(tty_stream)
|
||||||
|
assert AnsiMode.ALWAYS.use_ansi_codes(non_tty_stream)
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ)
|
||||||
|
def test_ansi_mode_auto(self, tty_stream, non_tty_stream):
|
||||||
|
if "CLICOLOR" in os.environ:
|
||||||
|
del os.environ["CLICOLOR"]
|
||||||
|
assert AnsiMode.AUTO.use_ansi_codes(tty_stream)
|
||||||
|
assert not AnsiMode.AUTO.use_ansi_codes(non_tty_stream)
|
||||||
|
|
||||||
|
os.environ["CLICOLOR"] = "0"
|
||||||
|
assert not AnsiMode.AUTO.use_ansi_codes(tty_stream)
|
||||||
|
assert not AnsiMode.AUTO.use_ansi_codes(non_tty_stream)
|
@ -137,21 +137,20 @@ class TestCLIMainTestCase:
|
|||||||
|
|
||||||
class TestSetupConsoleHandlerTestCase:
|
class TestSetupConsoleHandlerTestCase:
|
||||||
|
|
||||||
def test_with_tty_verbose(self, logging_handler):
|
def test_with_console_formatter_verbose(self, logging_handler):
|
||||||
setup_console_handler(logging_handler, True)
|
setup_console_handler(logging_handler, True)
|
||||||
assert type(logging_handler.formatter) == ConsoleWarningFormatter
|
assert type(logging_handler.formatter) == ConsoleWarningFormatter
|
||||||
assert '%(name)s' in logging_handler.formatter._fmt
|
assert '%(name)s' in logging_handler.formatter._fmt
|
||||||
assert '%(funcName)s' in logging_handler.formatter._fmt
|
assert '%(funcName)s' in logging_handler.formatter._fmt
|
||||||
|
|
||||||
def test_with_tty_not_verbose(self, logging_handler):
|
def test_with_console_formatter_not_verbose(self, logging_handler):
|
||||||
setup_console_handler(logging_handler, False)
|
setup_console_handler(logging_handler, False)
|
||||||
assert type(logging_handler.formatter) == ConsoleWarningFormatter
|
assert type(logging_handler.formatter) == ConsoleWarningFormatter
|
||||||
assert '%(name)s' not in logging_handler.formatter._fmt
|
assert '%(name)s' not in logging_handler.formatter._fmt
|
||||||
assert '%(funcName)s' not in logging_handler.formatter._fmt
|
assert '%(funcName)s' not in logging_handler.formatter._fmt
|
||||||
|
|
||||||
def test_with_not_a_tty(self, logging_handler):
|
def test_without_console_formatter(self, logging_handler):
|
||||||
logging_handler.stream.isatty.return_value = False
|
setup_console_handler(logging_handler, False, use_console_formatter=False)
|
||||||
setup_console_handler(logging_handler, False)
|
|
||||||
assert type(logging_handler.formatter) == logging.Formatter
|
assert type(logging_handler.formatter) == logging.Formatter
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from threading import Lock
|
|||||||
|
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
|
|
||||||
|
from compose.cli.colors import AnsiMode
|
||||||
from compose.parallel import GlobalLimit
|
from compose.parallel import GlobalLimit
|
||||||
from compose.parallel import parallel_execute
|
from compose.parallel import parallel_execute
|
||||||
from compose.parallel import parallel_execute_iter
|
from compose.parallel import parallel_execute_iter
|
||||||
@ -156,7 +157,7 @@ def test_parallel_execute_alignment(capsys):
|
|||||||
|
|
||||||
def test_parallel_execute_ansi(capsys):
|
def test_parallel_execute_ansi(capsys):
|
||||||
ParallelStreamWriter.instance = None
|
ParallelStreamWriter.instance = None
|
||||||
ParallelStreamWriter.set_noansi(value=False)
|
ParallelStreamWriter.set_default_ansi_mode(AnsiMode.ALWAYS)
|
||||||
results, errors = parallel_execute(
|
results, errors = parallel_execute(
|
||||||
objects=["something", "something more"],
|
objects=["something", "something more"],
|
||||||
func=lambda x: x,
|
func=lambda x: x,
|
||||||
@ -172,7 +173,7 @@ def test_parallel_execute_ansi(capsys):
|
|||||||
|
|
||||||
def test_parallel_execute_noansi(capsys):
|
def test_parallel_execute_noansi(capsys):
|
||||||
ParallelStreamWriter.instance = None
|
ParallelStreamWriter.instance = None
|
||||||
ParallelStreamWriter.set_noansi()
|
ParallelStreamWriter.set_default_ansi_mode(AnsiMode.NEVER)
|
||||||
results, errors = parallel_execute(
|
results, errors = parallel_execute(
|
||||||
objects=["something", "something more"],
|
objects=["something", "something more"],
|
||||||
func=lambda x: x,
|
func=lambda x: x,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user