From 7882f1fb06da723d957f9038c8a4f64e0cc451a0 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Wed, 9 Aug 2017 16:46:47 -0700 Subject: [PATCH] Keep no-ansi parameter in the CLI scope Signed-off-by: Joffrey F --- compose/cli/command.py | 5 ++--- compose/cli/main.py | 7 +++++++ compose/parallel.py | 33 +++++++++++++++++++-------------- compose/project.py | 26 +++++++++++--------------- compose/service.py | 6 ------ tests/acceptance/cli_test.py | 8 ++++++++ tests/unit/parallel_test.py | 5 +++-- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/compose/cli/command.py b/compose/cli/command.py index f5330d1c2..e1ae690c0 100644 --- a/compose/cli/command.py +++ b/compose/cli/command.py @@ -31,7 +31,6 @@ def project_from_options(project_dir, options): get_config_path_from_options(project_dir, options, environment), project_name=options.get('--project-name'), verbose=options.get('--verbose'), - noansi=options.get('--no-ansi'), host=host, tls_config=tls_config_from_options(options), 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, - 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: environment = Environment.from_env_file(project_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): - 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): diff --git a/compose/cli/main.py b/compose/cli/main.py index c0cf8747f..2bb53f95e 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -98,6 +98,7 @@ def dispatch(): options, handler, command_options = dispatcher.parse(sys.argv[1:]) setup_console_handler(console_handler, options.get('--verbose')) + setup_parallel_logger(options.get('--no-ansi')) return functools.partial(perform_command, options, handler, command_options) @@ -127,6 +128,12 @@ def setup_logging(): 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): if handler.stream.isatty(): format_class = ConsoleWarningFormatter diff --git a/compose/parallel.py b/compose/parallel.py index 89d074e35..1cf1fb094 100644 --- a/compose/parallel.py +++ b/compose/parallel.py @@ -26,7 +26,7 @@ log = logging.getLogger(__name__) 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 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) stream = get_output_stream(sys.stderr) - writer = ParallelStreamWriter(stream, msg, noansi) + writer = ParallelStreamWriter(stream, msg) for obj in objects: writer.add_object(get_name(obj)) writer.write_initial() @@ -221,12 +221,17 @@ class ParallelStreamWriter(object): 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.msg = msg self.lines = [] self.width = 0 - self.noansi = noansi def add_object(self, obj_index): self.lines.append(obj_index) @@ -267,27 +272,27 @@ class ParallelStreamWriter(object): self._write_ansi(obj_index, status) -def parallel_operation(containers, operation, options, message, noansi=False): +def parallel_operation(containers, operation, options, message): parallel_execute( containers, operator.methodcaller(operation, **options), operator.attrgetter('name'), 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] - parallel_operation(stopped_containers, 'remove', options, 'Removing', noansi=noansi) + parallel_operation(stopped_containers, 'remove', options, 'Removing') -def parallel_pause(containers, options, noansi=False): - parallel_operation(containers, 'pause', options, 'Pausing', noansi=noansi) +def parallel_pause(containers, options): + parallel_operation(containers, 'pause', options, 'Pausing') -def parallel_unpause(containers, options, noansi=False): - parallel_operation(containers, 'unpause', options, 'Unpausing', noansi=noansi) +def parallel_unpause(containers, options): + parallel_operation(containers, 'unpause', options, 'Unpausing') -def parallel_kill(containers, options, noansi=False): - parallel_operation(containers, 'kill', options, 'Killing', noansi=noansi) +def parallel_kill(containers, options): + parallel_operation(containers, 'kill', options, 'Killing') diff --git a/compose/project.py b/compose/project.py index 9ea6ff6bb..86fbda6ee 100644 --- a/compose/project.py +++ b/compose/project.py @@ -60,15 +60,13 @@ class Project(object): """ A collection of services. """ - def __init__(self, name, services, client, networks=None, volumes=None, config_version=None, - noansi=False): + def __init__(self, name, services, client, networks=None, volumes=None, config_version=None): self.name = name self.services = services self.client = client self.volumes = volumes or ProjectVolumes({}) self.networks = networks or ProjectNetworks({}, False) self.config_version = config_version - self.noansi = noansi def labels(self, one_off=OneOffFilter.exclude): labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)] @@ -77,7 +75,7 @@ class Project(object): return labels @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. """ @@ -88,7 +86,7 @@ class Project(object): networks, use_networking) 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: service_dict = dict(service_dict) @@ -128,7 +126,6 @@ class Project(object): volumes_from=volumes_from, secrets=secrets, pid_mode=pid_mode, - noansi=noansi, **service_dict) ) @@ -274,7 +271,7 @@ class Project(object): operator.attrgetter('name'), 'Starting', get_deps, - noansi=self.noansi) + ) return containers @@ -293,25 +290,25 @@ class Project(object): operator.attrgetter('name'), 'Stopping', get_deps, - noansi=self.noansi) + ) def pause(self, service_names=None, **options): containers = self.containers(service_names) - parallel.parallel_pause(reversed(containers), options, noansi=self.noansi) + parallel.parallel_pause(reversed(containers), options) return containers def unpause(self, service_names=None, **options): containers = self.containers(service_names) - parallel.parallel_unpause(containers, options, noansi=self.noansi) + parallel.parallel_unpause(containers, options) return containers 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): parallel.parallel_remove(self.containers( service_names, stopped=True, one_off=one_off - ), options, noansi=self.noansi) + ), options) def down(self, remove_image_type, include_volumes, remove_orphans=False): self.stop(one_off=OneOffFilter.include) @@ -337,7 +334,7 @@ class Project(object): self.build_container_operation_with_timeout_func('restart', options), operator.attrgetter('name'), 'Restarting', - noansi=self.noansi) + ) return containers 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'), None, get_deps, - noansi=self.noansi, ) if errors: raise ProjectError( @@ -508,7 +504,7 @@ class Project(object): operator.attrgetter('name'), 'Pulling', limit=5, - noansi=self.noansi) + ) else: for service in services: service.pull(ignore_pull_failures, silent=silent) diff --git a/compose/service.py b/compose/service.py index 22aae08b7..c43f635b2 100644 --- a/compose/service.py +++ b/compose/service.py @@ -158,7 +158,6 @@ class Service(object): secrets=None, scale=None, pid_mode=None, - noansi=False, **options ): self.name = name @@ -172,7 +171,6 @@ class Service(object): self.networks = networks or {} self.secrets = secrets or [] self.scale_num = scale or 1 - self.noansi = noansi self.options = options def __repr__(self): @@ -395,7 +393,6 @@ class Service(object): lambda n: create_and_start(self, n), lambda n: self.get_container_name(n), "Creating", - noansi=self.noansi, ) for error in errors.values(): raise OperationFailedError(error) @@ -417,7 +414,6 @@ class Service(object): recreate, lambda c: c.name, "Recreating", - noansi=self.noansi, ) for error in errors.values(): raise OperationFailedError(error) @@ -438,7 +434,6 @@ class Service(object): lambda c: self.start_container_if_stopped(c, attach_logs=not detached), lambda c: c.name, "Starting", - noansi=self.noansi, ) for error in errors.values(): @@ -460,7 +455,6 @@ class Service(object): stop_and_remove, lambda c: c.name, "Stopping and removing", - noansi=self.noansi, ) def execute_convergence_plan(self, plan, timeout=None, detached=False, diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index f7ecba9f5..adf645c2f 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -751,6 +751,14 @@ class CLITestCase(DockerClientTestCase): for service in services: 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() def test_up_with_default_network_config(self): filename = 'default-network-config.yml' diff --git a/tests/unit/parallel_test.py b/tests/unit/parallel_test.py index 519c66669..f82858eab 100644 --- a/tests/unit/parallel_test.py +++ b/tests/unit/parallel_test.py @@ -8,6 +8,7 @@ from docker.errors import APIError from compose.parallel import parallel_execute from compose.parallel import parallel_execute_iter +from compose.parallel import ParallelStreamWriter from compose.parallel import UpstreamError @@ -62,7 +63,7 @@ def test_parallel_execute_with_limit(): limit=limit, ) - assert results == tasks*[None] + assert results == tasks * [None] assert errors == {} @@ -133,12 +134,12 @@ def test_parallel_execute_alignment(capsys): def test_parallel_execute_alignment_noansi(capsys): + ParallelStreamWriter.set_noansi() results, errors = parallel_execute( objects=["short", "a very long name"], func=lambda x: x, get_name=six.text_type, msg="Aligning", - noansi=True, ) assert errors == {}