diff options
-rw-r--r-- | docs/changelog/1909.feature.rst | 2 | ||||
-rw-r--r-- | src/tox/session/cmd/run/common.py | 51 | ||||
-rw-r--r-- | src/tox/session/cmd/run/parallel.py | 2 | ||||
-rw-r--r-- | src/tox/session/cmd/run/sequential.py | 2 | ||||
-rw-r--r-- | src/tox/util/spinner.py | 14 | ||||
-rw-r--r-- | tests/session/cmd/test_parallel.py | 10 | ||||
-rw-r--r-- | tests/tox_env/python/virtual_env/test_setuptools.py | 2 |
7 files changed, 53 insertions, 30 deletions
diff --git a/docs/changelog/1909.feature.rst b/docs/changelog/1909.feature.rst new file mode 100644 index 00000000..a48f2757 --- /dev/null +++ b/docs/changelog/1909.feature.rst @@ -0,0 +1,2 @@ +Do not display status update environment reports when interrupted or for the final environment ran (because at the +final report will be soon printed and makes the status update redundant) - by :user:`gaborbernat`. diff --git a/src/tox/session/cmd/run/common.py b/src/tox/session/cmd/run/common.py index fcfdd49d..d39b0279 100644 --- a/src/tox/session/cmd/run/common.py +++ b/src/tox/session/cmd/run/common.py @@ -163,7 +163,7 @@ def report(start: float, runs: List[ToxEnvRunResult], is_colored: bool) -> int: logger = logging.getLogger(__name__) -def execute(state: State, max_workers: Optional[int], spinner: bool, live: bool) -> int: +def execute(state: State, max_workers: Optional[int], has_spinner: bool, live: bool) -> int: interrupt, done = Event(), Event() results: List[ToxEnvRunResult] = [] future_to_env: Dict["Future[ToxEnvRunResult]", ToxEnv] = {} @@ -173,6 +173,7 @@ def execute(state: State, max_workers: Optional[int], spinner: bool, live: bool) to_run_list.append(env) previous, has_previous = None, False try: + spinner = ToxSpinner(has_spinner, state, len(to_run_list)) try: thread = Thread( target=_queue_and_wait, @@ -183,6 +184,7 @@ def execute(state: State, max_workers: Optional[int], spinner: bool, live: bool) thread.join() except KeyboardInterrupt: previous, has_previous = signal(SIGINT, Handlers.SIG_IGN), True + spinner.print_report = False # no need to print reports at this point, final report coming up logger.error(f"[{os.getpid()}] KeyboardInterrupt - teardown started") interrupt.set() for future, tox_env in list(future_to_env.items()): @@ -236,12 +238,12 @@ def _queue_and_wait( interrupt: Event, done: Event, max_workers: Optional[int], - has_spinner: bool, + spinner: ToxSpinner, live: bool, ) -> None: try: options = state.options - with ToxSpinner(has_spinner, state, len(to_run_list)) as spinner: + with spinner: max_workers = len(to_run_list) if max_workers is None else max_workers completed: Set[str] = set() envs_to_run_generator = ready_to_run_envs(state, to_run_list, completed) @@ -252,11 +254,8 @@ def _queue_and_wait( try: executor = ThreadPoolExecutor(max_workers=max_workers, thread_name_prefix="tox-driver") + env_list: List[str] = [] while True: - env_list: List[str] = next(envs_to_run_generator, []) - if not env_list and not future_to_env: - break - for env in env_list: # queue all available tox_env_to_run = state.tox_env(env) if interrupt.is_set(): # queue the rest as failed upfront @@ -267,17 +266,33 @@ def _queue_and_wait( else: future = executor.submit(_run, tox_env_to_run) future_to_env[future] = tox_env_to_run - future = next(as_completed(future_to_env)) - tox_env_done = future_to_env.pop(future) - try: - result: ToxEnvRunResult = future.result() - except CancelledError: - tox_env_done.teardown() - name = tox_env_done.conf.name - result = ToxEnvRunResult(name=name, skipped=False, code=-3, outcomes=[], duration=MISS_DURATION) - results.append(result) - completed.add(result.name) - _handle_one_run_done(result, spinner, state, live) + + if not future_to_env: + result: Optional[ToxEnvRunResult] = None + else: # if we have queued wait for completed + future = next(as_completed(future_to_env)) + tox_env_done = future_to_env.pop(future) + try: + result = future.result() + except CancelledError: + tox_env_done.teardown() + name = tox_env_done.conf.name + result = ToxEnvRunResult( + name=name, skipped=False, code=-3, outcomes=[], duration=MISS_DURATION + ) + results.append(result) + completed.add(result.name) + + env_list = next(envs_to_run_generator, []) + # if nothing running and nothing more to run we're done + final_run = not env_list and not future_to_env + if final_run: # disable report on final env + spinner.print_report = False + if result is not None: + _handle_one_run_done(result, spinner, state, live) + if final_run: + break + except BaseException: # pragma: no cover # noqa logging.exception("Internal Error") # pragma: no cover raise # pragma: no cover diff --git a/src/tox/session/cmd/run/parallel.py b/src/tox/session/cmd/run/parallel.py index ffab1a3e..d68d84ba 100644 --- a/src/tox/session/cmd/run/parallel.py +++ b/src/tox/session/cmd/run/parallel.py @@ -74,6 +74,6 @@ def run_parallel(state: State) -> int: return execute( state, max_workers=state.options.parallel, - spinner=state.options.parallel_no_spinner is False and state.options.parallel_live is False, + has_spinner=state.options.parallel_no_spinner is False and state.options.parallel_live is False, live=state.options.parallel_live, ) diff --git a/src/tox/session/cmd/run/sequential.py b/src/tox/session/cmd/run/sequential.py index e2f1ef8e..ee935e56 100644 --- a/src/tox/session/cmd/run/sequential.py +++ b/src/tox/session/cmd/run/sequential.py @@ -17,4 +17,4 @@ def tox_add_option(parser: ToxParser) -> None: def run_sequential(state: State) -> int: - return execute(state, max_workers=1, spinner=False, live=True) + return execute(state, max_workers=1, has_spinner=False, live=True) diff --git a/src/tox/util/spinner.py b/src/tox/util/spinner.py index 527e2914..19cb0e33 100644 --- a/src/tox/util/spinner.py +++ b/src/tox/util/spinner.py @@ -54,6 +54,7 @@ class Spinner: self.frames = self.UNICODE_FRAMES if _file_support_encoding(self.UNICODE_FRAMES, stream) else self.ASCII_FRAMES self.stream = stream self.total = total + self.print_report = True self._envs: Dict[str, float] = OrderedDict() self._frame_index = 0 @@ -128,12 +129,13 @@ class Spinner: start_at = self._envs.pop(key, None) if self.enabled: self.clear() - duration = MISS_DURATION if start_at is None else time.monotonic() - start_at - base = f"{key}: {status} in {td_human_readable(duration)}" - if self.is_colored: - base = f"{color}{base}{Fore.RESET}" - base += os.linesep - self.stream.write(base) + if self.print_report: + duration = MISS_DURATION if start_at is None else time.monotonic() - start_at + base = f"{key}: {status} in {td_human_readable(duration)}" + if self.is_colored: + base = f"{color}{base}{Fore.RESET}" + base += os.linesep + self.stream.write(base) def disable_cursor(self) -> None: if self.stream.isatty(): diff --git a/tests/session/cmd/test_parallel.py b/tests/session/cmd/test_parallel.py index 4b590c9c..79d8dc25 100644 --- a/tests/session/cmd/test_parallel.py +++ b/tests/session/cmd/test_parallel.py @@ -63,6 +63,7 @@ def test_parallel_general(tox_project: ToxProjectCreator, monkeypatch: MonkeyPat out = outcome.out oks, skips, fails = {"a", "b", "c"}, {"d"}, {"e", "f"} + missing = set() for env in "a", "b", "c", "d", "e", "f": if env in ("c", "e"): assert "run c" in out, out @@ -73,11 +74,14 @@ def test_parallel_general(tox_project: ToxProjectCreator, monkeypatch: MonkeyPat of_type = "OK" if env in oks else ("SKIP" if env in skips else "FAIL") of_type_icon = "✔" if env in oks else ("⚠" if env in skips else "✖") env_done = f"{env}: {of_type} {of_type_icon}" - assert env_done in out, out - + is_missing = env_done not in out + if is_missing: + missing.add(env_done) env_report = f" {env}: {of_type} {'code 1 ' if env in fails else ''}(" assert env_report in out, out - assert out.index(env_done) < out.index(env_report), out + if not is_missing: + assert out.index(env_done) < out.index(env_report), out + assert len(missing) == 1, out def test_parallel_run_live_out(tox_project: ToxProjectCreator) -> None: diff --git a/tests/tox_env/python/virtual_env/test_setuptools.py b/tests/tox_env/python/virtual_env/test_setuptools.py index f7bdfe0a..33d0aecb 100644 --- a/tests/tox_env/python/virtual_env/test_setuptools.py +++ b/tests/tox_env/python/virtual_env/test_setuptools.py @@ -40,7 +40,7 @@ def test_setuptools_package( result = outcome.out.split("\n") py_messages = [i for i in result if "py: " in i] - assert len(py_messages) == 6, "\n".join(py_messages) # 1 install wheel + 3 command + 1 report + 1 final report + assert len(py_messages) == 5, "\n".join(py_messages) # 1 install wheel + 3 command + 1 final report package_messages = [i for i in result if ".pkg: " in i] # 1 install requires + 1 build requires + 1 build meta + 1 build isolated + 1 exit |