From 1bc4170e63ca430a4f5e8532a53a71a060d115fa Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 29 Apr 2020 17:58:33 +0300 Subject: [PATCH] terminalwriter: don't flush implicitly; add explicit flushes Flushing on every write is somewhat expensive. Rely on line buffering instead (if line buffering for stdout is disabled, there must be some reason...), and add explicit flushes when not outputting lines. This is how regular `print()` e.g. work so should be familiar. --- src/_pytest/_io/terminalwriter.py | 8 ++++++-- src/_pytest/python.py | 2 +- src/_pytest/runner.py | 1 + src/_pytest/setuponly.py | 2 ++ src/_pytest/terminal.py | 18 ++++++++++++------ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 5db4dc8b6..7bd8507c2 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -155,7 +155,7 @@ class TerminalWriter: self.line(line, **kw) - def write(self, msg: str, **kw: bool) -> None: + def write(self, msg: str, *, flush: bool = False, **kw: bool) -> None: if msg: current_line = msg.rsplit("\n", 1)[-1] if "\n" in msg: @@ -170,12 +170,16 @@ class TerminalWriter: else: markupmsg = msg self._file.write(markupmsg) - self._file.flush() + if flush: + self.flush() def line(self, s: str = "", **kw: bool) -> None: self.write(s, **kw) self.write("\n") + def flush(self) -> None: + self._file.flush() + def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: """Write lines of source code possibly highlighted. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 2b9bf4f5b..f472354ef 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1424,7 +1424,7 @@ def _showfixtures_main(config, session): def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: for line in doc.split("\n"): - tw.write(indent + line + "\n") + tw.line(indent + line) class Function(PyobjMixin, nodes.Item): diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index f87ccb461..76785ada7 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -120,6 +120,7 @@ def show_test_item(item): used_fixtures = sorted(getattr(item, "fixturenames", [])) if used_fixtures: tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) + tw.flush() def pytest_runtest_setup(item): diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index aa5a95ff9..c9cc589ff 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -68,6 +68,8 @@ def _show_fixture_action(fixturedef, msg): if hasattr(fixturedef, "cached_param"): tw.write("[{}]".format(fixturedef.cached_param)) + tw.flush() + if capman: capman.resume_global_capture() diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 52c04a49c..d46456733 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -343,7 +343,7 @@ class TerminalReporter: fspath = self.startdir.bestrelpath(fspath) self._tw.line() self._tw.write(fspath + " ") - self._tw.write(res, **markup) + self._tw.write(res, flush=True, **markup) def write_ensure_prefix(self, prefix, extra="", **kwargs): if self.currentfspath != prefix: @@ -359,8 +359,11 @@ class TerminalReporter: self._tw.line() self.currentfspath = None - def write(self, content, **markup): - self._tw.write(content, **markup) + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + self._tw.write(content, flush=flush, **markup) + + def flush(self) -> None: + self._tw.flush() def write_line(self, line, **markup): if not isinstance(line, str): @@ -437,9 +440,11 @@ class TerminalReporter: if self.showlongtestinfo: line = self._locationline(nodeid, *location) self.write_ensure_prefix(line, "") + self.flush() elif self.showfspath: fsid = nodeid.split("::")[0] self.write_fspath_result(fsid, "") + self.flush() def pytest_runtest_logreport(self, report: TestReport) -> None: self._tests_ran = True @@ -491,6 +496,7 @@ class TerminalReporter: self._tw.write(word, **markup) self._tw.write(" " + line) self.currentfspath = -2 + self.flush() @property def _is_last_item(self): @@ -539,7 +545,7 @@ class TerminalReporter: msg = self._get_progress_information_message() w = self._width_of_current_line fill = self._tw.fullwidth - w - 1 - self.write(msg.rjust(fill), **{color: True}) + self.write(msg.rjust(fill), flush=True, **{color: True}) @property def _width_of_current_line(self): @@ -553,10 +559,10 @@ class TerminalReporter: def pytest_collection(self) -> None: if self.isatty: if self.config.option.verbose >= 0: - self.write("collecting ... ", bold=True) + self.write("collecting ... ", flush=True, bold=True) self._collect_report_last_write = time.time() elif self.config.option.verbose >= 1: - self.write("collecting ... ", bold=True) + self.write("collecting ... ", flush=True, bold=True) def pytest_collectreport(self, report: CollectReport) -> None: if report.failed: