- Add patch tidy-up-embeddedfile.patch based on an upstream PR. Can

be dropped when updating.

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:pytest/python-pytest?expand=0&rev=55
This commit is contained in:
Steve Kowalik 2020-03-18 07:47:14 +00:00 committed by Git OBS Bridge
parent 1e1e42c326
commit 623732992d
3 changed files with 251 additions and 0 deletions

View File

@ -1,3 +1,9 @@
-------------------------------------------------------------------
Wed Mar 18 07:45:23 UTC 2020 - Steve Kowalik <steven.kowalik@suse.com>
- Add patch tidy-up-embeddedfile.patch based on an upstream PR. Can
be dropped when updating.
-------------------------------------------------------------------
Wed Feb 19 09:47:59 UTC 2020 - Ondřej Súkup <mimi.vx@gmail.com>

View File

@ -33,6 +33,8 @@ Summary: Python testing tool with autodiscovery and detailed asserts
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 @@ pytest is a cross-project Python testing tool. It provides:
%prep
%setup -q -n pytest-%{version}
%autopatch -p1
%build
%python_build

242
tidy-up-embeddedfile.patch Normal file
View File

@ -0,0 +1,242 @@
From 29e4cb5d45f44379aba948c2cd791b3b97210e31 Mon Sep 17 00:00:00 2001
From: Ran Benita <ran@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,10 @@
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
import pytest
from _pytest import capture
@@ -838,49 +834,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 +1252,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 +1328,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 +1433,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()