Hello community, here is the log from the commit of package python-pytest for openSUSE:Factory checked in at 2020-04-19 21:42:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest (Old) and /work/SRC/openSUSE:Factory/.python-pytest.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest" Sun Apr 19 21:42:03 2020 rev:54 rq:786286 version:5.3.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest/python-pytest.changes 2020-02-24 15:49:50.611129424 +0100 +++ /work/SRC/openSUSE:Factory/.python-pytest.new.2738/python-pytest.changes 2020-04-19 21:42:18.143217933 +0200 @@ -1,0 +2,6 @@ +Wed Mar 18 07:45:23 UTC 2020 - Steve Kowalik <steven.kowa...@suse.com> + +- Add patch tidy-up-embeddedfile.patch based on an upstream PR. Can + be dropped when updating. + +------------------------------------------------------------------- New: ---- tidy-up-embeddedfile.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest.spec ++++++ --- /var/tmp/diff_new_pack.I327kp/_old 2020-04-19 21:42:22.115225903 +0200 +++ /var/tmp/diff_new_pack.I327kp/_new 2020-04-19 21:42:22.119225911 +0200 @@ -33,6 +33,8 @@ License: MIT URL: https://github.com/pytest-dev/pytest Source: https://files.pythonhosted.org/packages/source/p/pytest/pytest-%{version}.tar.gz +# UPSTREAM FIX: gh/pytest-dev#6899 +Patch0: tidy-up-embeddedfile.patch BuildRequires: %{python_module setuptools >= 40.0} BuildRequires: %{python_module setuptools_scm} BuildRequires: fdupes @@ -90,6 +92,7 @@ %prep %setup -q -n pytest-%{version} +%autopatch -p1 %build %python_build ++++++ tidy-up-embeddedfile.patch ++++++ >From 29e4cb5d45f44379aba948c2cd791b3b97210e31 Mon Sep 17 00:00:00 2001 From: Ran Benita <r...@unusedvar.com> Date: Sat, 7 Mar 2020 18:38:22 +0200 Subject: [PATCH] Remove safe_text_dupfile() and simplify EncodedFile I tried to understand what the `safe_text_dupfile()` function and `EncodedFile` class do. Outside tests, `EncodedFile` is only used by `safe_text_dupfile`, and `safe_text_dupfile` is only used by `FDCaptureBinary.__init__()`. I then started to eliminate always-true conditions based on the single call site, and in the end nothing was left except of a couple workarounds that are still needed. --- src/_pytest/capture.py | 66 ++++++++++-------------------------- testing/test_capture.py | 75 ++++++----------------------------------- 2 files changed, 28 insertions(+), 113 deletions(-) Index: pytest-5.3.5/src/_pytest/capture.py =================================================================== --- pytest-5.3.5.orig/src/_pytest/capture.py +++ pytest-5.3.5/src/_pytest/capture.py @@ -391,57 +391,21 @@ class CaptureFixture: yield -def safe_text_dupfile(f, mode, default_encoding="UTF8"): - """ return an open text file object that's a duplicate of f on the - FD-level if possible. - """ - encoding = getattr(f, "encoding", None) - try: - fd = f.fileno() - except Exception: - if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"): - # we seem to have a text stream, let's just use it - return f - else: - newfd = os.dup(fd) - if "b" not in mode: - mode += "b" - f = os.fdopen(newfd, mode, 0) # no buffering - return EncodedFile(f, encoding or default_encoding) - - -class EncodedFile: - errors = "strict" # possibly needed by py3 code (issue555) - - def __init__(self, buffer, encoding): - self.buffer = buffer - self.encoding = encoding - - def write(self, obj): - if isinstance(obj, str): - obj = obj.encode(self.encoding, "replace") - else: - raise TypeError( - "write() argument must be str, not {}".format(type(obj).__name__) - ) - self.buffer.write(obj) - - def writelines(self, linelist): - data = "".join(linelist) - self.write(data) +class EncodedFile(io.TextIOWrapper): + __slots__ = () @property def name(self): - """Ensure that file.name is a string.""" + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 return repr(self.buffer) @property def mode(self): + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. return self.buffer.mode.replace("b", "") - def __getattr__(self, name): - return getattr(object.__getattribute__(self, "buffer"), name) - CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"]) @@ -555,9 +519,12 @@ class FDCaptureBinary: self.syscapture = SysCapture(targetfd) else: if tmpfile is None: - f = TemporaryFile() - with f: - tmpfile = safe_text_dupfile(f, mode="wb+") + tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + write_through=True, + ) if targetfd in patchsysdict: self.syscapture = SysCapture(targetfd, tmpfile) else: @@ -582,7 +549,7 @@ class FDCaptureBinary: def snap(self): self.tmpfile.seek(0) - res = self.tmpfile.read() + res = self.tmpfile.buffer.read() self.tmpfile.seek(0) self.tmpfile.truncate() return res @@ -624,10 +591,10 @@ class FDCapture(FDCaptureBinary): EMPTY_BUFFER = str() # type: ignore def snap(self): - res = super().snap() - enc = getattr(self.tmpfile, "encoding", None) - if enc and isinstance(res, bytes): - res = str(res, enc, "replace") + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() return res Index: pytest-5.3.5/testing/test_capture.py =================================================================== --- pytest-5.3.5.orig/testing/test_capture.py +++ pytest-5.3.5/testing/test_capture.py @@ -1,14 +1,11 @@ import contextlib import io import os -import pickle import subprocess import sys import textwrap -from io import StringIO from io import UnsupportedOperation -from typing import List -from typing import TextIO +from typing import BinaryIO import pytest from _pytest import capture @@ -838,49 +835,6 @@ def tmpfile(testdir): f.close() -@needsosdup -def test_dupfile(tmpfile) -> None: - flist = [] # type: List[TextIO] - for i in range(5): - nf = capture.safe_text_dupfile(tmpfile, "wb") - assert nf != tmpfile - assert nf.fileno() != tmpfile.fileno() - assert nf not in flist - print(i, end="", file=nf) - flist.append(nf) - - fname_open = flist[0].name - assert fname_open == repr(flist[0].buffer) - - for i in range(5): - f = flist[i] - f.close() - fname_closed = flist[0].name - assert fname_closed == repr(flist[0].buffer) - assert fname_closed != fname_open - tmpfile.seek(0) - s = tmpfile.read() - assert "01234" in repr(s) - tmpfile.close() - assert fname_closed == repr(flist[0].buffer) - - -def test_dupfile_on_bytesio(): - bio = io.BytesIO() - f = capture.safe_text_dupfile(bio, "wb") - f.write("hello") - assert bio.getvalue() == b"hello" - assert "BytesIO object" in f.name - - -def test_dupfile_on_textio(): - sio = StringIO() - f = capture.safe_text_dupfile(sio, "wb") - f.write("hello") - assert sio.getvalue() == "hello" - assert not hasattr(f, "name") - - @contextlib.contextmanager def lsof_check(): pid = os.getpid() @@ -1299,8 +1253,8 @@ def test_error_attribute_issue555(testdi """ import sys def test_capattr(): - assert sys.stdout.errors == "strict" - assert sys.stderr.errors == "strict" + assert sys.stdout.errors == "replace" + assert sys.stderr.errors == "replace" """ ) reprec = testdir.inline_run() @@ -1375,15 +1329,6 @@ def test_crash_on_closing_tmpfile_py27(t result.stdout.no_fnmatch_line("*IOError*") -def test_pickling_and_unpickling_encoded_file(): - # See https://bitbucket.org/pytest-dev/pytest/pull-request/194 - # pickle.loads() raises infinite recursion if - # EncodedFile.__getattr__ is not implemented properly - ef = capture.EncodedFile(None, None) - ef_as_str = pickle.dumps(ef) - pickle.loads(ef_as_str) - - def test_global_capture_with_live_logging(testdir): # Issue 3819 # capture should work with live cli logging @@ -1489,6 +1434,19 @@ def test_typeerror_encodedfile_write(tes result_with_capture = testdir.runpytest(str(p)) assert result_with_capture.ret == result_without_capture.ret - result_with_capture.stdout.fnmatch_lines( - ["E * TypeError: write() argument must be str, not bytes"] + out = result_with_capture.stdout.str() + assert ("TypeError: write() argument must be str, not bytes" in out) or ( + "TypeError: unicode argument expected, got 'bytes'" in out ) + +def test_encodedfile_writelines(tmpfile: BinaryIO) -> None: + ef = capture.EncodedFile(tmpfile, encoding="utf-8") + with pytest.raises(TypeError): + ef.writelines([b"line1", b"line2"]) + assert ef.writelines(["line3", "line4"]) is None # type: ignore[func-returns-value] # noqa: F821 + ef.flush() + tmpfile.seek(0) + assert tmpfile.read() == b"line3line4" + tmpfile.close() + with pytest.raises(ValueError): + ef.read()