Keep no-ansi parameter in the CLI scope

Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
Joffrey F 2017-08-09 16:46:47 -07:00 committed by Joffrey F
parent 444d888720
commit 7882f1fb06
7 changed files with 50 additions and 40 deletions

View File

@ -31,7 +31,6 @@ def project_from_options(project_dir, options):
get_config_path_from_options(project_dir, options, environment), get_config_path_from_options(project_dir, options, environment),
project_name=options.get('--project-name'), project_name=options.get('--project-name'),
verbose=options.get('--verbose'), verbose=options.get('--verbose'),
noansi=options.get('--no-ansi'),
host=host, host=host,
tls_config=tls_config_from_options(options), tls_config=tls_config_from_options(options),
environment=environment, environment=environment,
@ -82,7 +81,7 @@ def get_client(environment, verbose=False, version=None, tls_config=None, host=N
def get_project(project_dir, config_path=None, project_name=None, verbose=False, def get_project(project_dir, config_path=None, project_name=None, verbose=False,
noansi=False, host=None, tls_config=None, environment=None, override_dir=None): host=None, tls_config=None, environment=None, override_dir=None):
if not environment: if not environment:
environment = Environment.from_env_file(project_dir) environment = Environment.from_env_file(project_dir)
config_details = config.find(project_dir, config_path, environment, override_dir) config_details = config.find(project_dir, config_path, environment, override_dir)
@ -101,7 +100,7 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
) )
with errors.handle_connection_errors(client): with errors.handle_connection_errors(client):
return Project.from_config(project_name, config_data, client, noansi=noansi) return Project.from_config(project_name, config_data, client)
def get_project_name(working_dir, project_name=None, environment=None): def get_project_name(working_dir, project_name=None, environment=None):

View File

@ -98,6 +98,7 @@ def dispatch():
options, handler, command_options = dispatcher.parse(sys.argv[1:]) options, handler, command_options = dispatcher.parse(sys.argv[1:])
setup_console_handler(console_handler, options.get('--verbose')) setup_console_handler(console_handler, options.get('--verbose'))
setup_parallel_logger(options.get('--no-ansi'))
return functools.partial(perform_command, options, handler, command_options) return functools.partial(perform_command, options, handler, command_options)
@ -127,6 +128,12 @@ def setup_logging():
logging.getLogger("requests").propagate = False logging.getLogger("requests").propagate = False
def setup_parallel_logger(noansi):
if noansi:
import compose.parallel
compose.parallel.ParallelStreamWriter.set_noansi()
def setup_console_handler(handler, verbose): def setup_console_handler(handler, verbose):
if handler.stream.isatty(): if handler.stream.isatty():
format_class = ConsoleWarningFormatter format_class = ConsoleWarningFormatter

View File

@ -26,7 +26,7 @@ log = logging.getLogger(__name__)
STOP = object() STOP = object()
def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None, noansi=False): def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None):
"""Runs func on objects in parallel while ensuring that func is """Runs func on objects in parallel while ensuring that func is
ran on object only after it is ran on all its dependencies. ran on object only after it is ran on all its dependencies.
@ -36,7 +36,7 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None, limit=None, no
objects = list(objects) objects = list(objects)
stream = get_output_stream(sys.stderr) stream = get_output_stream(sys.stderr)
writer = ParallelStreamWriter(stream, msg, noansi) writer = ParallelStreamWriter(stream, msg)
for obj in objects: for obj in objects:
writer.add_object(get_name(obj)) writer.add_object(get_name(obj))
writer.write_initial() writer.write_initial()
@ -221,12 +221,17 @@ class ParallelStreamWriter(object):
to jump to the correct line, and write over the line. to jump to the correct line, and write over the line.
""" """
def __init__(self, stream, msg, noansi): noansi = False
@classmethod
def set_noansi(cls, value=True):
cls.noansi = value
def __init__(self, stream, msg):
self.stream = stream self.stream = stream
self.msg = msg self.msg = msg
self.lines = [] self.lines = []
self.width = 0 self.width = 0
self.noansi = noansi
def add_object(self, obj_index): def add_object(self, obj_index):
self.lines.append(obj_index) self.lines.append(obj_index)
@ -267,27 +272,27 @@ class ParallelStreamWriter(object):
self._write_ansi(obj_index, status) self._write_ansi(obj_index, status)
def parallel_operation(containers, operation, options, message, noansi=False): def parallel_operation(containers, operation, options, message):
parallel_execute( parallel_execute(
containers, containers,
operator.methodcaller(operation, **options), operator.methodcaller(operation, **options),
operator.attrgetter('name'), operator.attrgetter('name'),
message, message,
noansi=noansi) )
def parallel_remove(containers, options, noansi=False): def parallel_remove(containers, options):
stopped_containers = [c for c in containers if not c.is_running] stopped_containers = [c for c in containers if not c.is_running]
parallel_operation(stopped_containers, 'remove', options, 'Removing', noansi=noansi) parallel_operation(stopped_containers, 'remove', options, 'Removing')
def parallel_pause(containers, options, noansi=False): def parallel_pause(containers, options):
parallel_operation(containers, 'pause', options, 'Pausing', noansi=noansi) parallel_operation(containers, 'pause', options, 'Pausing')
def parallel_unpause(containers, options, noansi=False): def parallel_unpause(containers, options):
parallel_operation(containers, 'unpause', options, 'Unpausing', noansi=noansi) parallel_operation(containers, 'unpause', options, 'Unpausing')
def parallel_kill(containers, options, noansi=False): def parallel_kill(containers, options):
parallel_operation(containers, 'kill', options, 'Killing', noansi=noansi) parallel_operation(containers, 'kill', options, 'Killing')

View File

@ -60,15 +60,13 @@ class Project(object):
""" """
A collection of services. A collection of services.
""" """
def __init__(self, name, services, client, networks=None, volumes=None, config_version=None, def __init__(self, name, services, client, networks=None, volumes=None, config_version=None):
noansi=False):
self.name = name self.name = name
self.services = services self.services = services
self.client = client self.client = client
self.volumes = volumes or ProjectVolumes({}) self.volumes = volumes or ProjectVolumes({})
self.networks = networks or ProjectNetworks({}, False) self.networks = networks or ProjectNetworks({}, False)
self.config_version = config_version self.config_version = config_version
self.noansi = noansi
def labels(self, one_off=OneOffFilter.exclude): def labels(self, one_off=OneOffFilter.exclude):
labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)] labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)]
@ -77,7 +75,7 @@ class Project(object):
return labels return labels
@classmethod @classmethod
def from_config(cls, name, config_data, client, noansi=False): def from_config(cls, name, config_data, client):
""" """
Construct a Project from a config.Config object. Construct a Project from a config.Config object.
""" """
@ -88,7 +86,7 @@ class Project(object):
networks, networks,
use_networking) use_networking)
volumes = ProjectVolumes.from_config(name, config_data, client) volumes = ProjectVolumes.from_config(name, config_data, client)
project = cls(name, [], client, project_networks, volumes, config_data.version, noansi=noansi) project = cls(name, [], client, project_networks, volumes, config_data.version)
for service_dict in config_data.services: for service_dict in config_data.services:
service_dict = dict(service_dict) service_dict = dict(service_dict)
@ -128,7 +126,6 @@ class Project(object):
volumes_from=volumes_from, volumes_from=volumes_from,
secrets=secrets, secrets=secrets,
pid_mode=pid_mode, pid_mode=pid_mode,
noansi=noansi,
**service_dict) **service_dict)
) )
@ -274,7 +271,7 @@ class Project(object):
operator.attrgetter('name'), operator.attrgetter('name'),
'Starting', 'Starting',
get_deps, get_deps,
noansi=self.noansi) )
return containers return containers
@ -293,25 +290,25 @@ class Project(object):
operator.attrgetter('name'), operator.attrgetter('name'),
'Stopping', 'Stopping',
get_deps, get_deps,
noansi=self.noansi) )
def pause(self, service_names=None, **options): def pause(self, service_names=None, **options):
containers = self.containers(service_names) containers = self.containers(service_names)
parallel.parallel_pause(reversed(containers), options, noansi=self.noansi) parallel.parallel_pause(reversed(containers), options)
return containers return containers
def unpause(self, service_names=None, **options): def unpause(self, service_names=None, **options):
containers = self.containers(service_names) containers = self.containers(service_names)
parallel.parallel_unpause(containers, options, noansi=self.noansi) parallel.parallel_unpause(containers, options)
return containers return containers
def kill(self, service_names=None, **options): def kill(self, service_names=None, **options):
parallel.parallel_kill(self.containers(service_names), options, noansi=self.noansi) parallel.parallel_kill(self.containers(service_names), options)
def remove_stopped(self, service_names=None, one_off=OneOffFilter.exclude, **options): def remove_stopped(self, service_names=None, one_off=OneOffFilter.exclude, **options):
parallel.parallel_remove(self.containers( parallel.parallel_remove(self.containers(
service_names, stopped=True, one_off=one_off service_names, stopped=True, one_off=one_off
), options, noansi=self.noansi) ), options)
def down(self, remove_image_type, include_volumes, remove_orphans=False): def down(self, remove_image_type, include_volumes, remove_orphans=False):
self.stop(one_off=OneOffFilter.include) self.stop(one_off=OneOffFilter.include)
@ -337,7 +334,7 @@ class Project(object):
self.build_container_operation_with_timeout_func('restart', options), self.build_container_operation_with_timeout_func('restart', options),
operator.attrgetter('name'), operator.attrgetter('name'),
'Restarting', 'Restarting',
noansi=self.noansi) )
return containers return containers
def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, build_args=None): def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, build_args=None):
@ -454,7 +451,6 @@ class Project(object):
operator.attrgetter('name'), operator.attrgetter('name'),
None, None,
get_deps, get_deps,
noansi=self.noansi,
) )
if errors: if errors:
raise ProjectError( raise ProjectError(
@ -508,7 +504,7 @@ class Project(object):
operator.attrgetter('name'), operator.attrgetter('name'),
'Pulling', 'Pulling',
limit=5, limit=5,
noansi=self.noansi) )
else: else:
for service in services: for service in services:
service.pull(ignore_pull_failures, silent=silent) service.pull(ignore_pull_failures, silent=silent)

View File

@ -158,7 +158,6 @@ class Service(object):
secrets=None, secrets=None,
scale=None, scale=None,
pid_mode=None, pid_mode=None,
noansi=False,
**options **options
): ):
self.name = name self.name = name
@ -172,7 +171,6 @@ class Service(object):
self.networks = networks or {} self.networks = networks or {}
self.secrets = secrets or [] self.secrets = secrets or []
self.scale_num = scale or 1 self.scale_num = scale or 1
self.noansi = noansi
self.options = options self.options = options
def __repr__(self): def __repr__(self):
@ -395,7 +393,6 @@ class Service(object):
lambda n: create_and_start(self, n), lambda n: create_and_start(self, n),
lambda n: self.get_container_name(n), lambda n: self.get_container_name(n),
"Creating", "Creating",
noansi=self.noansi,
) )
for error in errors.values(): for error in errors.values():
raise OperationFailedError(error) raise OperationFailedError(error)
@ -417,7 +414,6 @@ class Service(object):
recreate, recreate,
lambda c: c.name, lambda c: c.name,
"Recreating", "Recreating",
noansi=self.noansi,
) )
for error in errors.values(): for error in errors.values():
raise OperationFailedError(error) raise OperationFailedError(error)
@ -438,7 +434,6 @@ class Service(object):
lambda c: self.start_container_if_stopped(c, attach_logs=not detached), lambda c: self.start_container_if_stopped(c, attach_logs=not detached),
lambda c: c.name, lambda c: c.name,
"Starting", "Starting",
noansi=self.noansi,
) )
for error in errors.values(): for error in errors.values():
@ -460,7 +455,6 @@ class Service(object):
stop_and_remove, stop_and_remove,
lambda c: c.name, lambda c: c.name,
"Stopping and removing", "Stopping and removing",
noansi=self.noansi,
) )
def execute_convergence_plan(self, plan, timeout=None, detached=False, def execute_convergence_plan(self, plan, timeout=None, detached=False,

View File

@ -751,6 +751,14 @@ class CLITestCase(DockerClientTestCase):
for service in services: for service in services:
assert self.lookup(container, service.name) assert self.lookup(container, service.name)
@v2_only()
def test_up_no_ansi(self):
self.base_dir = 'tests/fixtures/v2-simple'
result = self.dispatch(['--no-ansi', 'up', '-d'], None)
assert "%c[2K\r" % 27 not in result.stderr
assert "%c[1A" % 27 not in result.stderr
assert "%c[1B" % 27 not in result.stderr
@v2_only() @v2_only()
def test_up_with_default_network_config(self): def test_up_with_default_network_config(self):
filename = 'default-network-config.yml' filename = 'default-network-config.yml'

View File

@ -8,6 +8,7 @@ from docker.errors import APIError
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
from compose.parallel import ParallelStreamWriter
from compose.parallel import UpstreamError from compose.parallel import UpstreamError
@ -62,7 +63,7 @@ def test_parallel_execute_with_limit():
limit=limit, limit=limit,
) )
assert results == tasks*[None] assert results == tasks * [None]
assert errors == {} assert errors == {}
@ -133,12 +134,12 @@ def test_parallel_execute_alignment(capsys):
def test_parallel_execute_alignment_noansi(capsys): def test_parallel_execute_alignment_noansi(capsys):
ParallelStreamWriter.set_noansi()
results, errors = parallel_execute( results, errors = parallel_execute(
objects=["short", "a very long name"], objects=["short", "a very long name"],
func=lambda x: x, func=lambda x: x,
get_name=six.text_type, get_name=six.text_type,
msg="Aligning", msg="Aligning",
noansi=True,
) )
assert errors == {} assert errors == {}