aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/_io
diff options
context:
space:
mode:
authorarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-02-09 12:00:52 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 15:58:17 +0300
commit8e1413fed79d1e8036e65228af6c93399ccf5502 (patch)
tree502c9df7b2614d20541c7a2d39d390e9a51877cc /contrib/python/pytest/py3/_pytest/_io
parent6b813c17d56d1d05f92c61ddc347d0e4d358fe85 (diff)
downloadydb-8e1413fed79d1e8036e65228af6c93399ccf5502.tar.gz
intermediate changes
ref:614ed510ddd3cdf86a8c5dbf19afd113397e0172
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/_io')
-rw-r--r--contrib/python/pytest/py3/_pytest/_io/__init__.py43
-rw-r--r--contrib/python/pytest/py3/_pytest/_io/saferepr.py60
-rw-r--r--contrib/python/pytest/py3/_pytest/_io/terminalwriter.py210
-rw-r--r--contrib/python/pytest/py3/_pytest/_io/wcwidth.py55
4 files changed, 314 insertions, 54 deletions
diff --git a/contrib/python/pytest/py3/_pytest/_io/__init__.py b/contrib/python/pytest/py3/_pytest/_io/__init__.py
index f56579806c..db001e918c 100644
--- a/contrib/python/pytest/py3/_pytest/_io/__init__.py
+++ b/contrib/python/pytest/py3/_pytest/_io/__init__.py
@@ -1,39 +1,8 @@
-from typing import List
-from typing import Sequence
+from .terminalwriter import get_terminal_width
+from .terminalwriter import TerminalWriter
-from py.io import TerminalWriter as BaseTerminalWriter # noqa: F401
-
-class TerminalWriter(BaseTerminalWriter):
- def _write_source(self, lines: List[str], indents: Sequence[str] = ()) -> None:
- """Write lines of source code possibly highlighted.
-
- Keeping this private for now because the API is clunky. We should discuss how
- to evolve the terminal writer so we can have more precise color support, for example
- being able to write part of a line in one color and the rest in another, and so on.
- """
- if indents and len(indents) != len(lines):
- raise ValueError(
- "indents size ({}) should have same size as lines ({})".format(
- len(indents), len(lines)
- )
- )
- if not indents:
- indents = [""] * len(lines)
- source = "\n".join(lines)
- new_lines = self._highlight(source).splitlines()
- for indent, new_line in zip(indents, new_lines):
- self.line(indent + new_line)
-
- def _highlight(self, source):
- """Highlight the given source code according to the "code_highlight" option"""
- if not self.hasmarkup:
- return source
- try:
- from pygments.formatters.terminal import TerminalFormatter
- from pygments.lexers.python import PythonLexer
- from pygments import highlight
- except ImportError:
- return source
- else:
- return highlight(source, PythonLexer(), TerminalFormatter(bg="dark"))
+__all__ = [
+ "TerminalWriter",
+ "get_terminal_width",
+]
diff --git a/contrib/python/pytest/py3/_pytest/_io/saferepr.py b/contrib/python/pytest/py3/_pytest/_io/saferepr.py
index 47a00de606..5eb1e08890 100644
--- a/contrib/python/pytest/py3/_pytest/_io/saferepr.py
+++ b/contrib/python/pytest/py3/_pytest/_io/saferepr.py
@@ -1,9 +1,12 @@
import pprint
import reprlib
from typing import Any
+from typing import Dict
+from typing import IO
+from typing import Optional
-def _try_repr_or_str(obj):
+def _try_repr_or_str(obj: object) -> str:
try:
return repr(obj)
except (KeyboardInterrupt, SystemExit):
@@ -12,7 +15,7 @@ def _try_repr_or_str(obj):
return '{}("{}")'.format(type(obj).__name__, obj)
-def _format_repr_exception(exc: BaseException, obj: Any) -> str:
+def _format_repr_exception(exc: BaseException, obj: object) -> str:
try:
exc_info = _try_repr_or_str(exc)
except (KeyboardInterrupt, SystemExit):
@@ -33,16 +36,15 @@ def _ellipsize(s: str, maxsize: int) -> str:
class SafeRepr(reprlib.Repr):
- """subclass of repr.Repr that limits the resulting size of repr()
- and includes information on exceptions raised during the call.
- """
+ """repr.Repr that limits the resulting size of repr() and includes
+ information on exceptions raised during the call."""
def __init__(self, maxsize: int) -> None:
super().__init__()
self.maxstring = maxsize
self.maxsize = maxsize
- def repr(self, x: Any) -> str:
+ def repr(self, x: object) -> str:
try:
s = super().repr(x)
except (KeyboardInterrupt, SystemExit):
@@ -51,7 +53,7 @@ class SafeRepr(reprlib.Repr):
s = _format_repr_exception(exc, x)
return _ellipsize(s, self.maxsize)
- def repr_instance(self, x: Any, level: int) -> str:
+ def repr_instance(self, x: object, level: int) -> str:
try:
s = repr(x)
except (KeyboardInterrupt, SystemExit):
@@ -61,8 +63,9 @@ class SafeRepr(reprlib.Repr):
return _ellipsize(s, self.maxsize)
-def safeformat(obj: Any) -> str:
- """return a pretty printed string for the given object.
+def safeformat(obj: object) -> str:
+ """Return a pretty printed string for the given object.
+
Failing __repr__ functions of user instances will be represented
with a short exception info.
"""
@@ -72,12 +75,15 @@ def safeformat(obj: Any) -> str:
return _format_repr_exception(exc, obj)
-def saferepr(obj: Any, maxsize: int = 240) -> str:
- """return a size-limited safe repr-string for the given object.
+def saferepr(obj: object, maxsize: int = 240) -> str:
+ """Return a size-limited safe repr-string for the given object.
+
Failing __repr__ functions of user instances will be represented
with a short exception info and 'saferepr' generally takes
- care to never raise exceptions itself. This function is a wrapper
- around the Repr/reprlib functionality of the standard 2.6 lib.
+ care to never raise exceptions itself.
+
+ This function is a wrapper around the Repr/reprlib functionality of the
+ standard 2.6 lib.
"""
return SafeRepr(maxsize).repr(obj)
@@ -85,19 +91,39 @@ def saferepr(obj: Any, maxsize: int = 240) -> str:
class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
"""PrettyPrinter that always dispatches (regardless of width)."""
- def _format(self, object, stream, indent, allowance, context, level):
- p = self._dispatch.get(type(object).__repr__, None)
+ def _format(
+ self,
+ object: object,
+ stream: IO[str],
+ indent: int,
+ allowance: int,
+ context: Dict[int, Any],
+ level: int,
+ ) -> None:
+ # Type ignored because _dispatch is private.
+ p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined]
objid = id(object)
if objid in context or p is None:
- return super()._format(object, stream, indent, allowance, context, level)
+ # Type ignored because _format is private.
+ super()._format( # type: ignore[misc]
+ object, stream, indent, allowance, context, level,
+ )
+ return
context[objid] = 1
p(self, object, stream, indent, allowance, context, level + 1)
del context[objid]
-def _pformat_dispatch(object, indent=1, width=80, depth=None, *, compact=False):
+def _pformat_dispatch(
+ object: object,
+ indent: int = 1,
+ width: int = 80,
+ depth: Optional[int] = None,
+ *,
+ compact: bool = False,
+) -> str:
return AlwaysDispatchingPrettyPrinter(
indent=indent, width=width, depth=depth, compact=compact
).pformat(object)
diff --git a/contrib/python/pytest/py3/_pytest/_io/terminalwriter.py b/contrib/python/pytest/py3/_pytest/_io/terminalwriter.py
new file mode 100644
index 0000000000..8edf4cd75f
--- /dev/null
+++ b/contrib/python/pytest/py3/_pytest/_io/terminalwriter.py
@@ -0,0 +1,210 @@
+"""Helper functions for writing to terminals and files."""
+import os
+import shutil
+import sys
+from typing import Optional
+from typing import Sequence
+from typing import TextIO
+
+from .wcwidth import wcswidth
+from _pytest.compat import final
+
+
+# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
+
+
+def get_terminal_width() -> int:
+ width, _ = shutil.get_terminal_size(fallback=(80, 24))
+
+ # The Windows get_terminal_size may be bogus, let's sanify a bit.
+ if width < 40:
+ width = 80
+
+ return width
+
+
+def should_do_markup(file: TextIO) -> bool:
+ if os.environ.get("PY_COLORS") == "1":
+ return True
+ if os.environ.get("PY_COLORS") == "0":
+ return False
+ if "NO_COLOR" in os.environ:
+ return False
+ if "FORCE_COLOR" in os.environ:
+ return True
+ return (
+ hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb"
+ )
+
+
+@final
+class TerminalWriter:
+ _esctable = dict(
+ black=30,
+ red=31,
+ green=32,
+ yellow=33,
+ blue=34,
+ purple=35,
+ cyan=36,
+ white=37,
+ Black=40,
+ Red=41,
+ Green=42,
+ Yellow=43,
+ Blue=44,
+ Purple=45,
+ Cyan=46,
+ White=47,
+ bold=1,
+ light=2,
+ blink=5,
+ invert=7,
+ )
+
+ def __init__(self, file: Optional[TextIO] = None) -> None:
+ if file is None:
+ file = sys.stdout
+ if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32":
+ try:
+ import colorama
+ except ImportError:
+ pass
+ else:
+ file = colorama.AnsiToWin32(file).stream
+ assert file is not None
+ self._file = file
+ self.hasmarkup = should_do_markup(file)
+ self._current_line = ""
+ self._terminal_width: Optional[int] = None
+ self.code_highlight = True
+
+ @property
+ def fullwidth(self) -> int:
+ if self._terminal_width is not None:
+ return self._terminal_width
+ return get_terminal_width()
+
+ @fullwidth.setter
+ def fullwidth(self, value: int) -> None:
+ self._terminal_width = value
+
+ @property
+ def width_of_current_line(self) -> int:
+ """Return an estimate of the width so far in the current line."""
+ return wcswidth(self._current_line)
+
+ def markup(self, text: str, **markup: bool) -> str:
+ for name in markup:
+ if name not in self._esctable:
+ raise ValueError(f"unknown markup: {name!r}")
+ if self.hasmarkup:
+ esc = [self._esctable[name] for name, on in markup.items() if on]
+ if esc:
+ text = "".join("\x1b[%sm" % cod for cod in esc) + text + "\x1b[0m"
+ return text
+
+ def sep(
+ self,
+ sepchar: str,
+ title: Optional[str] = None,
+ fullwidth: Optional[int] = None,
+ **markup: bool,
+ ) -> None:
+ if fullwidth is None:
+ fullwidth = self.fullwidth
+ # The goal is to have the line be as long as possible
+ # under the condition that len(line) <= fullwidth.
+ if sys.platform == "win32":
+ # If we print in the last column on windows we are on a
+ # new line but there is no way to verify/neutralize this
+ # (we may not know the exact line width).
+ # So let's be defensive to avoid empty lines in the output.
+ fullwidth -= 1
+ if title is not None:
+ # we want 2 + 2*len(fill) + len(title) <= fullwidth
+ # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth
+ # 2*len(sepchar)*N <= fullwidth - len(title) - 2
+ # N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
+ N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1)
+ fill = sepchar * N
+ line = f"{fill} {title} {fill}"
+ else:
+ # we want len(sepchar)*N <= fullwidth
+ # i.e. N <= fullwidth // len(sepchar)
+ line = sepchar * (fullwidth // len(sepchar))
+ # In some situations there is room for an extra sepchar at the right,
+ # in particular if we consider that with a sepchar like "_ " the
+ # trailing space is not important at the end of the line.
+ if len(line) + len(sepchar.rstrip()) <= fullwidth:
+ line += sepchar.rstrip()
+
+ self.line(line, **markup)
+
+ def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None:
+ if msg:
+ current_line = msg.rsplit("\n", 1)[-1]
+ if "\n" in msg:
+ self._current_line = current_line
+ else:
+ self._current_line += current_line
+
+ msg = self.markup(msg, **markup)
+
+ try:
+ self._file.write(msg)
+ except UnicodeEncodeError:
+ # Some environments don't support printing general Unicode
+ # strings, due to misconfiguration or otherwise; in that case,
+ # print the string escaped to ASCII.
+ # When the Unicode situation improves we should consider
+ # letting the error propagate instead of masking it (see #7475
+ # for one brief attempt).
+ msg = msg.encode("unicode-escape").decode("ascii")
+ self._file.write(msg)
+
+ if flush:
+ self.flush()
+
+ def line(self, s: str = "", **markup: bool) -> None:
+ self.write(s, **markup)
+ 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.
+
+ Keeping this private for now because the API is clunky. We should discuss how
+ to evolve the terminal writer so we can have more precise color support, for example
+ being able to write part of a line in one color and the rest in another, and so on.
+ """
+ if indents and len(indents) != len(lines):
+ raise ValueError(
+ "indents size ({}) should have same size as lines ({})".format(
+ len(indents), len(lines)
+ )
+ )
+ if not indents:
+ indents = [""] * len(lines)
+ source = "\n".join(lines)
+ new_lines = self._highlight(source).splitlines()
+ for indent, new_line in zip(indents, new_lines):
+ self.line(indent + new_line)
+
+ def _highlight(self, source: str) -> str:
+ """Highlight the given source code if we have markup support."""
+ if not self.hasmarkup or not self.code_highlight:
+ return source
+ try:
+ from pygments.formatters.terminal import TerminalFormatter
+ from pygments.lexers.python import PythonLexer
+ from pygments import highlight
+ except ImportError:
+ return source
+ else:
+ highlighted: str = highlight(
+ source, PythonLexer(), TerminalFormatter(bg="dark")
+ )
+ return highlighted
diff --git a/contrib/python/pytest/py3/_pytest/_io/wcwidth.py b/contrib/python/pytest/py3/_pytest/_io/wcwidth.py
new file mode 100644
index 0000000000..e5c7bf4d86
--- /dev/null
+++ b/contrib/python/pytest/py3/_pytest/_io/wcwidth.py
@@ -0,0 +1,55 @@
+import unicodedata
+from functools import lru_cache
+
+
+@lru_cache(100)
+def wcwidth(c: str) -> int:
+ """Determine how many columns are needed to display a character in a terminal.
+
+ Returns -1 if the character is not printable.
+ Returns 0, 1 or 2 for other characters.
+ """
+ o = ord(c)
+
+ # ASCII fast path.
+ if 0x20 <= o < 0x07F:
+ return 1
+
+ # Some Cf/Zp/Zl characters which should be zero-width.
+ if (
+ o == 0x0000
+ or 0x200B <= o <= 0x200F
+ or 0x2028 <= o <= 0x202E
+ or 0x2060 <= o <= 0x2063
+ ):
+ return 0
+
+ category = unicodedata.category(c)
+
+ # Control characters.
+ if category == "Cc":
+ return -1
+
+ # Combining characters with zero width.
+ if category in ("Me", "Mn"):
+ return 0
+
+ # Full/Wide east asian characters.
+ if unicodedata.east_asian_width(c) in ("F", "W"):
+ return 2
+
+ return 1
+
+
+def wcswidth(s: str) -> int:
+ """Determine how many columns are needed to display a string in a terminal.
+
+ Returns -1 if the string contains non-printable characters.
+ """
+ width = 0
+ for c in unicodedata.normalize("NFC", s):
+ wc = wcwidth(c)
+ if wc < 0:
+ return -1
+ width += wc
+ return width