From 0f74bcca1be79cc0bf84c417460c5f2e34a4a4e0 Mon Sep 17 00:00:00 2001 From: Orion Poplawski Date: Tue, 15 Nov 2016 14:31:09 -0700 Subject: [PATCH] Add upstream patch to fix python 3.5 compat - Add patch to allow docutils to read unicode source - Update spec --- python-nose-py35.patch | 33 +++++++++ python-nose-readunicode.patch | 20 ++++++ python-nose-unicode.patch | 128 ++++++++++++++++++++++++++++++++++ python-nose.spec | 44 +++++++----- 4 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 python-nose-py35.patch create mode 100644 python-nose-readunicode.patch create mode 100644 python-nose-unicode.patch diff --git a/python-nose-py35.patch b/python-nose-py35.patch new file mode 100644 index 0000000..604f60e --- /dev/null +++ b/python-nose-py35.patch @@ -0,0 +1,33 @@ +diff -up nose-1.3.7/functional_tests/test_load_tests_from_test_case.py.py35 nose-1.3.7/functional_tests/test_load_tests_from_test_case.py +--- nose-1.3.7/functional_tests/test_load_tests_from_test_case.py.py35 2012-09-29 02:18:54.000000000 -0600 ++++ nose-1.3.7/functional_tests/test_load_tests_from_test_case.py 2016-11-15 13:42:27.946707472 -0700 +@@ -29,6 +29,7 @@ class NoFixturePlug(Plugin): + pass + def tearDown(self): + pass ++ Derived.__qualname__ = Derived.__name__ + # must use nose loader here because the default loader in 2.3 + # won't load tests from base classes + l = loader.TestLoader() +diff -up nose-1.3.7/nose/util.py.py35 nose-1.3.7/nose/util.py +--- nose-1.3.7/nose/util.py.py35 2015-04-04 02:52:52.000000000 -0600 ++++ nose-1.3.7/nose/util.py 2016-11-15 13:42:27.946707472 -0700 +@@ -643,6 +643,7 @@ def transplant_class(cls, module): + pass + C.__module__ = module + C.__name__ = cls.__name__ ++ C.__qualname__ = cls.__name__ + return C + + +diff -up nose-1.3.7/unit_tests/test_xunit.py.py35 nose-1.3.7/unit_tests/test_xunit.py +--- nose-1.3.7/unit_tests/test_xunit.py.py35 2015-04-04 02:52:52.000000000 -0600 ++++ nose-1.3.7/unit_tests/test_xunit.py 2016-11-15 13:42:27.946707472 -0700 +@@ -16,6 +16,7 @@ def mktest(): + class TC(unittest.TestCase): + def runTest(self): + pass ++ TC.__qualname__ = TC.__name__ + test = TC() + return test + diff --git a/python-nose-readunicode.patch b/python-nose-readunicode.patch new file mode 100644 index 0000000..61d4cd2 --- /dev/null +++ b/python-nose-readunicode.patch @@ -0,0 +1,20 @@ +diff -up nose-1.3.7/nose/plugins/doctests.py.readunicode nose-1.3.7/nose/plugins/doctests.py +--- nose-1.3.7/nose/plugins/doctests.py.readunicode 2015-04-04 02:52:52.000000000 -0600 ++++ nose-1.3.7/nose/plugins/doctests.py 2016-11-15 14:24:54.298239018 -0700 +@@ -49,6 +49,7 @@ test. + """ + from __future__ import generators + ++import codecs + import logging + import os + import sys +@@ -259,7 +260,7 @@ class Doctest(Plugin): + """ + if self.extension and anyp(filename.endswith, self.extension): + name = os.path.basename(filename) +- dh = open(filename) ++ dh = codecs.open(filename, encoding='utf-8') + try: + doc = dh.read() + finally: diff --git a/python-nose-unicode.patch b/python-nose-unicode.patch new file mode 100644 index 0000000..a103da8 --- /dev/null +++ b/python-nose-unicode.patch @@ -0,0 +1,128 @@ +diff -up nose-1.3.7/AUTHORS.unicode nose-1.3.7/AUTHORS +diff -up nose-1.3.7/CHANGELOG.unicode nose-1.3.7/CHANGELOG +diff -up nose-1.3.7/nose/plugins/capture.py.unicode nose-1.3.7/nose/plugins/capture.py +--- nose-1.3.7/nose/plugins/capture.py.unicode 2015-04-04 02:52:52.000000000 -0600 ++++ nose-1.3.7/nose/plugins/capture.py 2016-11-15 13:58:18.713025335 -0700 +@@ -12,6 +12,7 @@ the options ``-s`` or ``--nocapture``. + import logging + import os + import sys ++import traceback + from nose.plugins.base import Plugin + from nose.pyversion import exc_to_unicode, force_unicode + from nose.util import ln +@@ -71,26 +72,56 @@ class Capture(Plugin): + def formatError(self, test, err): + """Add captured output to error report. + """ +- test.capturedOutput = output = self.buffer ++ test.capturedOutput = output = '' ++ output_exc_info = None ++ try: ++ test.capturedOutput = output = self.buffer ++ except UnicodeError: ++ # python2's StringIO.StringIO [1] class has this warning: ++ # ++ # The StringIO object can accept either Unicode or 8-bit strings, ++ # but mixing the two may take some care. If both are used, 8-bit ++ # strings that cannot be interpreted as 7-bit ASCII (that use the ++ # 8th bit) will cause a UnicodeError to be raised when getvalue() ++ # is called. ++ # ++ # This exception handler is a protection against issue #816 [2]. ++ # Capturing the exception info allows us to display it back to the ++ # user. ++ # ++ # [1] ++ # [2] ++ output_exc_info = sys.exc_info() + self._buf = None +- if not output: ++ if (not output) and (not output_exc_info): + # Don't return None as that will prevent other + # formatters from formatting and remove earlier formatters + # formats, instead return the err we got + return err + ec, ev, tb = err +- return (ec, self.addCaptureToErr(ev, output), tb) ++ return (ec, self.addCaptureToErr(ev, output, output_exc_info=output_exc_info), tb) + + def formatFailure(self, test, err): + """Add captured output to failure report. + """ + return self.formatError(test, err) + +- def addCaptureToErr(self, ev, output): ++ def addCaptureToErr(self, ev, output, output_exc_info=None): ++ # If given, output_exc_info should be a 3-tuple from sys.exc_info(), ++ # from an exception raised while trying to get the captured output. + ev = exc_to_unicode(ev) + output = force_unicode(output) +- return u'\n'.join([ev, ln(u'>> begin captured stdout <<'), +- output, ln(u'>> end captured stdout <<')]) ++ error_text = [ev, ln(u'>> begin captured stdout <<'), ++ output, ln(u'>> end captured stdout <<')] ++ if output_exc_info: ++ error_text.extend([u'OUTPUT ERROR: Could not get captured output.', ++ # ++ # ++ u"The test might've printed both 'unicode' strings and non-ASCII 8-bit 'str' strings.", ++ ln(u'>> begin captured stdout exception traceback <<'), ++ u''.join(traceback.format_exception(*output_exc_info)), ++ ln(u'>> end captured stdout exception traceback <<')]) ++ return u'\n'.join(error_text) + + def start(self): + self.stdout.append(sys.stdout) +diff -up nose-1.3.7/unit_tests/test_capture_plugin.py.unicode nose-1.3.7/unit_tests/test_capture_plugin.py +--- nose-1.3.7/unit_tests/test_capture_plugin.py.unicode 2012-09-29 02:18:54.000000000 -0600 ++++ nose-1.3.7/unit_tests/test_capture_plugin.py 2016-11-15 13:58:18.714025330 -0700 +@@ -4,6 +4,12 @@ import unittest + from optparse import OptionParser + from nose.config import Config + from nose.plugins.capture import Capture ++from nose.pyversion import force_unicode ++ ++if sys.version_info[0] == 2: ++ py2 = True ++else: ++ py2 = False + + class TestCapturePlugin(unittest.TestCase): + +@@ -62,6 +68,35 @@ class TestCapturePlugin(unittest.TestCas + c.end() + self.assertEqual(c.buffer, "test 日本\n") + ++ def test_does_not_crash_with_mixed_unicode_and_nonascii_str(self): ++ class Dummy: ++ pass ++ d = Dummy() ++ c = Capture() ++ c.start() ++ printed_nonascii_str = force_unicode("test 日本").encode('utf-8') ++ printed_unicode = force_unicode("Hello") ++ print printed_nonascii_str ++ print printed_unicode ++ try: ++ raise Exception("boom") ++ except: ++ err = sys.exc_info() ++ formatted = c.formatError(d, err) ++ _, fev, _ = formatted ++ ++ if py2: ++ for string in [force_unicode(printed_nonascii_str, encoding='utf-8'), printed_unicode]: ++ assert string not in fev, "Output unexpectedly found in error message" ++ assert d.capturedOutput == '', "capturedOutput unexpectedly non-empty" ++ assert "OUTPUT ERROR" in fev ++ assert "captured stdout exception traceback" in fev ++ assert "UnicodeDecodeError" in fev ++ else: ++ for string in [repr(printed_nonascii_str), printed_unicode]: ++ assert string in fev, "Output not found in error message" ++ assert string in d.capturedOutput, "Output not attached to test" ++ + def test_format_error(self): + class Dummy: + pass diff --git a/python-nose.spec b/python-nose.spec index c0a240f..e3f1e7f 100644 --- a/python-nose.spec +++ b/python-nose.spec @@ -17,7 +17,7 @@ Name: python-nose Version: 1.3.7 -Release: 9%{?dist} +Release: 10%{?dist} Summary: Discovery-based unittest extension for Python Group: Development/Languages @@ -27,6 +27,14 @@ Source0: http://pypi.python.org/packages/source/n/nose/nose-%{version}.ta # Make compatible with coverage 4.1 # https://github.com/nose-devs/nose/pull/1004 Patch0: python-nose-coverage4.patch +# Fix python 3.5 compat +# https://github.com/nose-devs/nose/pull/983 +Patch1: python-nose-py35.patch +# Fix UnicodeDecodeError with captured output +# https://github.com/nose-devs/nose/pull/988 +Patch2: python-nose-unicode.patch +# Allow docutils to read utf-8 source +Patch3: python-nose-readunicode.patch BuildArch: noarch BuildRequires: python2-devel @@ -97,6 +105,9 @@ python3 unittests. %prep %setup -q -n %{upstream_name}-%{version} %patch0 -p1 -b .coverage4 +%patch1 -p1 -b .py35 +%patch2 -p1 -b .unicode +%patch3 -p1 -b .unicode dos2unix examples/attrib_plugin.py @@ -106,30 +117,28 @@ cp -a . %{py3dir} %endif # with_python3 %build -%{__python} setup.py build +%py2_build %if 0%{?with_python3} pushd %{py3dir} -%{__python3} setup.py build +%py3_build popd %endif # with_python3 %install -rm -rf %{buildroot} # Must do the python3 install first because the scripts in /usr/bin are # overwritten with every setup.py install (and we want the python2 version # to be the default for now). %if 0%{?with_python3} pushd %{py3dir} -%{__python3} setup.py install --skip-build --root %{buildroot} +%py3_install rm %{buildroot}%{_bindir}/nosetests mkdir -m 0755 -p %{buildroot}%{_mandir}/man1/ mv %{buildroot}%{_prefix}/man/man1/nosetests.1 %{buildroot}%{_mandir}/man1/nosetests-%{python3_version}.1 popd %endif # with_python3 -%{__python} setup.py install --skip-build --root %{buildroot} \ - --install-data=%{_datadir} +%py2_install -- --install-data=%{_datadir} %if 0%{?with_docs} pushd doc @@ -144,25 +153,20 @@ rm -rf reST/.static reST/.templates %check -%{__python} selftest.py +%{__python2} selftest.py %if 0%{?with_python3} pushd %{py3dir} export PYTHONPATH=`pwd`/build/lib %{__python3} setup.py build_tests -# Various selftests fail with Python 3.5; yet nose is needed to rebuild other packages -# (reported upstream as https://github.com/nose-devs/nose/issues/928 \ -#%{__python3} selftest.py \ -# -v +%{__python3} selftest.py popd %endif # with_python3 -%clean -rm -rf %{buildroot} - %files -%doc AUTHORS CHANGELOG lgpl.txt NEWS README.txt +%license lgpl.txt +%doc AUTHORS CHANGELOG NEWS README.txt %{_bindir}/nosetests %{_bindir}/nosetests-%{python_version} %{_mandir}/man1/nosetests.1.gz @@ -170,7 +174,8 @@ rm -rf %{buildroot} %if 0%{?with_python3} %files -n python3-%{upstream_name} -%doc AUTHORS CHANGELOG lgpl.txt NEWS README.txt +%license lgpl.txt +%doc AUTHORS CHANGELOG NEWS README.txt %{_bindir}/nosetests-%{python3_version} %{_mandir}/man1/nosetests-%{python3_version}.1.gz %{python3_sitelib}/nose* @@ -183,6 +188,11 @@ rm -rf %{buildroot} %endif # with_docs %changelog +* Tue Nov 15 2016 Orion Poplawski 1.3.7-10 +- Add upstream patch to fix python 3.5 compat +- Add patch to allow docutils to read unicode source +- Update spec + * Wed Nov 9 2016 Orion Poplawski 1.3.7-9 - Add patch to fix build with coverage 4.1