From 03bc8aba4e818d79668adf9d0049b92b5f7c100e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 23 Jan 2020 12:45:38 +0100 Subject: [PATCH] config: typing for create_terminal_writer, re-export TerminalWriter This also imports `TerminalWriter` explicitly via `_pytest._io`, allowing for easier extending / replacing it. --- .pre-commit-config.yaml | 2 +- src/_pytest/_code/code.py | 23 ++++++++++++----------- src/_pytest/_io/__init__.py | 3 +++ src/_pytest/cacheprovider.py | 3 ++- src/_pytest/config/__init__.py | 9 +++++---- src/_pytest/doctest.py | 5 +++-- src/_pytest/fixtures.py | 3 ++- src/_pytest/reports.py | 3 ++- testing/code/test_excinfo.py | 7 ++++--- testing/logging/test_formatter.py | 7 +++---- 10 files changed, 37 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 978cfcde8..8894c713f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,7 +64,7 @@ repos: _code\.| builtin\.| code\.| - io\.(BytesIO|saferepr)| + io\.(BytesIO|saferepr|TerminalWriter)| path\.local\.sysfind| process\.| std\. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 3620970fd..94ad4292e 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -29,6 +29,7 @@ import pluggy import py import _pytest +from _pytest._io import TerminalWriter from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import saferepr from _pytest.compat import overload @@ -915,14 +916,14 @@ class TerminalRepr: # FYI this is called from pytest-xdist's serialization of exception # information. io = StringIO() - tw = py.io.TerminalWriter(file=io) + tw = TerminalWriter(file=io) self.toterminal(tw) return io.getvalue().strip() def __repr__(self) -> str: return "<{} instance at {:0x}>".format(self.__class__, id(self)) - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: raise NotImplementedError() @@ -933,7 +934,7 @@ class ExceptionRepr(TerminalRepr): def addsection(self, name: str, content: str, sep: str = "-") -> None: self.sections.append((name, content, sep)) - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: for name, content, sep in self.sections: tw.sep(sep, name) tw.line(content) @@ -953,7 +954,7 @@ class ExceptionChainRepr(ExceptionRepr): self.reprtraceback = chain[-1][0] self.reprcrash = chain[-1][1] - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: for element in self.chain: element[0].toterminal(tw) if element[2] is not None: @@ -970,7 +971,7 @@ class ReprExceptionInfo(ExceptionRepr): self.reprtraceback = reprtraceback self.reprcrash = reprcrash - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: self.reprtraceback.toterminal(tw) super().toterminal(tw) @@ -988,7 +989,7 @@ class ReprTraceback(TerminalRepr): self.extraline = extraline self.style = style - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: # the entries might have different styles for i, entry in enumerate(self.reprentries): if entry.style == "long": @@ -1020,7 +1021,7 @@ class ReprEntryNative(TerminalRepr): def __init__(self, tblines: Sequence[str]) -> None: self.lines = tblines - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: tw.write("".join(self.lines)) @@ -1039,7 +1040,7 @@ class ReprEntry(TerminalRepr): self.reprfileloc = filelocrepr self.style = style - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: if self.style == "short": assert self.reprfileloc is not None self.reprfileloc.toterminal(tw) @@ -1072,7 +1073,7 @@ class ReprFileLocation(TerminalRepr): self.lineno = lineno self.message = message - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: # filename and lineno output for each entry, # using an output format that most editors understand msg = self.message @@ -1087,7 +1088,7 @@ class ReprLocals(TerminalRepr): def __init__(self, lines: Sequence[str]) -> None: self.lines = lines - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: for line in self.lines: tw.line(line) @@ -1096,7 +1097,7 @@ class ReprFuncArgs(TerminalRepr): def __init__(self, args: Sequence[Tuple[str, object]]) -> None: self.args = args - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: if self.args: linesofar = "" for name, value in self.args: diff --git a/src/_pytest/_io/__init__.py b/src/_pytest/_io/__init__.py index e69de29bb..047bb179a 100644 --- a/src/_pytest/_io/__init__.py +++ b/src/_pytest/_io/__init__.py @@ -0,0 +1,3 @@ +# Reexport TerminalWriter from here instead of py, to make it easier to +# extend or swap our own implementation in the future. +from py.io import TerminalWriter as TerminalWriter # noqa: F401 diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 802c52122..97b51617b 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -17,6 +17,7 @@ from .pathlib import Path from .pathlib import resolve_from_str from .pathlib import rm_rf from _pytest import nodes +from _pytest._io import TerminalWriter from _pytest.config import Config from _pytest.main import Session @@ -418,7 +419,7 @@ def pytest_report_header(config): def cacheshow(config, session): from pprint import pformat - tw = py.io.TerminalWriter() + tw = TerminalWriter() tw.line("cachedir: " + str(config.cache._cachedir)) if not config.cache._cachedir.is_dir(): tw.line("cache is empty") diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 2677c2bec..3516b333e 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -36,6 +36,7 @@ from .findpaths import determine_setup from .findpaths import exists from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback +from _pytest._io import TerminalWriter from _pytest.compat import importlib_metadata from _pytest.compat import TYPE_CHECKING from _pytest.outcomes import fail @@ -73,7 +74,7 @@ def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]": config = _prepareconfig(args, plugins) except ConftestImportFailure as e: exc_info = ExceptionInfo(e.excinfo) - tw = py.io.TerminalWriter(sys.stderr) + tw = TerminalWriter(sys.stderr) tw.line( "ImportError while loading conftest '{e.path}'.".format(e=e), red=True ) @@ -99,7 +100,7 @@ def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]": finally: config._ensure_unconfigure() except UsageError as e: - tw = py.io.TerminalWriter(sys.stderr) + tw = TerminalWriter(sys.stderr) for msg in e.args: tw.line("ERROR: {}\n".format(msg), red=True) return ExitCode.USAGE_ERROR @@ -1175,12 +1176,12 @@ def setns(obj, dic): setattr(pytest, name, value) -def create_terminal_writer(config, *args, **kwargs): +def create_terminal_writer(config: Config, *args, **kwargs) -> TerminalWriter: """Create a TerminalWriter instance configured according to the options in the config object. Every code which requires a TerminalWriter object and has access to a config object should use this function. """ - tw = py.io.TerminalWriter(*args, **kwargs) + tw = TerminalWriter(*args, **kwargs) if config.option.color == "yes": tw.hasmarkup = True if config.option.color == "no": diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index d7ca888cc..c2fd3fd6d 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -13,13 +13,14 @@ from typing import Sequence from typing import Tuple from typing import Union -import py +import py.path import pytest from _pytest import outcomes from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter from _pytest.compat import safe_getattr from _pytest.compat import TYPE_CHECKING from _pytest.fixtures import FixtureRequest @@ -139,7 +140,7 @@ class ReprFailDoctest(TerminalRepr): ): self.reprlocation_lines = reprlocation_lines - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: for reprlocation, lines in self.reprlocation_lines: for line in lines: tw.line(line) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index f0a1a2ed0..464828de4 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -16,6 +16,7 @@ import py import _pytest from _pytest._code.code import FormattedExcinfo from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter from _pytest.compat import _format_args from _pytest.compat import _PytestWrapper from _pytest.compat import get_real_func @@ -754,7 +755,7 @@ class FixtureLookupErrorRepr(TerminalRepr): self.firstlineno = firstlineno self.argname = argname - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: # tw.line("FixtureLookupError: %s" %(self.argname), red=True) for tbline in self.tblines: tw.line(tbline.rstrip()) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 79e106a65..3ad67c224 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -18,6 +18,7 @@ from _pytest._code.code import ReprFuncArgs from _pytest._code.code import ReprLocals from _pytest._code.code import ReprTraceback from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter from _pytest.compat import TYPE_CHECKING from _pytest.nodes import Node from _pytest.outcomes import skip @@ -80,7 +81,7 @@ class BaseReport: .. versionadded:: 3.0 """ - tw = py.io.TerminalWriter(stringio=True) + tw = TerminalWriter(stringio=True) tw.hasmarkup = False self.toterminal(tw) exc = tw.stringio.getvalue() diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index ae5d30b3a..55e487fe3 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -12,6 +12,7 @@ import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo from _pytest._code.code import FormattedExcinfo +from _pytest._io import TerminalWriter try: @@ -855,7 +856,7 @@ raise ValueError() from _pytest._code.code import TerminalRepr class MyRepr(TerminalRepr): - def toterminal(self, tw: py.io.TerminalWriter) -> None: + def toterminal(self, tw: TerminalWriter) -> None: tw.line("я") x = str(MyRepr()) @@ -1005,7 +1006,7 @@ raise ValueError() """ ) excinfo = pytest.raises(ValueError, mod.f) - tw = py.io.TerminalWriter(stringio=True) + tw = TerminalWriter(stringio=True) repr = excinfo.getrepr(**reproptions) repr.toterminal(tw) assert tw.stringio.getvalue() @@ -1225,7 +1226,7 @@ raise ValueError() getattr(excinfo.value, attr).__traceback__ = None r = excinfo.getrepr() - tw = py.io.TerminalWriter(stringio=True) + tw = TerminalWriter(stringio=True) tw.hasmarkup = False r.toterminal(tw) diff --git a/testing/logging/test_formatter.py b/testing/logging/test_formatter.py index b363e8b03..85e949d7a 100644 --- a/testing/logging/test_formatter.py +++ b/testing/logging/test_formatter.py @@ -1,7 +1,6 @@ import logging -import py.io - +from _pytest._io import TerminalWriter from _pytest.logging import ColoredLevelFormatter @@ -22,7 +21,7 @@ def test_coloredlogformatter(): class option: pass - tw = py.io.TerminalWriter() + tw = TerminalWriter() tw.hasmarkup = True formatter = ColoredLevelFormatter(tw, logfmt) output = formatter.format(record) @@ -142,7 +141,7 @@ def test_colored_short_level(): class option: pass - tw = py.io.TerminalWriter() + tw = TerminalWriter() tw.hasmarkup = True formatter = ColoredLevelFormatter(tw, logfmt) output = formatter.format(record)