diff --git a/python-nose-r708-through-r111-py27-fixes.patch b/python-nose-r708-through-r111-py27-fixes.patch
new file mode 100644
index 0000000..b94af65
--- /dev/null
+++ b/python-nose-r708-through-r111-py27-fixes.patch
@@ -0,0 +1,316 @@
+diff -r 1b69f8a7f836 -r 7168c74ebb2e functional_tests/doc_tests/test_init_plugin/init_plugin.rst
+--- a/functional_tests/doc_tests/test_init_plugin/init_plugin.rst Fri Mar 05 15:31:57 2010 -0500
++++ b/functional_tests/doc_tests/test_init_plugin/init_plugin.rst Wed Jul 07 10:31:01 2010 -0400
+@@ -24,6 +24,7 @@
+
+ >>> import unittest
+ >>> class TestConfigurableWidget(unittest.TestCase):
++ ... longMessage = False
+ ... def setUp(self):
+ ... self.widget = ConfigurableWidget()
+ ... def test_can_frobnicate(self):
+diff -r 1b69f8a7f836 -r 7168c74ebb2e functional_tests/doc_tests/test_xunit_plugin/test_skips.rst
+--- a/functional_tests/doc_tests/test_xunit_plugin/test_skips.rst Fri Mar 05 15:31:57 2010 -0500
++++ b/functional_tests/doc_tests/test_xunit_plugin/test_skips.rst Wed Jul 07 10:31:01 2010 -0400
+@@ -37,4 +37,4 @@
+ FAILED (SKIP=1, errors=1, failures=1)
+
+ >>> open(outfile, 'r').read() # doctest: +ELLIPSIS
+-'.........'
++'.........'
+diff -r 1b69f8a7f836 -r 7168c74ebb2e functional_tests/test_collector.py
+--- a/functional_tests/test_collector.py Fri Mar 05 15:31:57 2010 -0500
++++ b/functional_tests/test_collector.py Wed Jul 07 10:31:01 2010 -0400
+@@ -13,7 +13,7 @@
+ self.result = _TextTestResult(
+ self.stream, self.descriptions, self.verbosity)
+ return self.result
+-
++
+
+ class TestNoseTestCollector(unittest.TestCase):
+
+@@ -27,7 +27,7 @@
+ warnings.filterwarnings(action='ignore',
+ category=RuntimeWarning,
+ module='nose.plugins.manager')
+-
++
+ try:
+ os.chdir(os.path.join(support, 'issue038'))
+ unittest.TestProgram(
+diff -r 1b69f8a7f836 -r 7168c74ebb2e nose/plugins/manager.py
+--- a/nose/plugins/manager.py Fri Mar 05 15:31:57 2010 -0500
++++ b/nose/plugins/manager.py Wed Jul 07 10:31:01 2010 -0400
+@@ -182,7 +182,7 @@
+ """Null Plugin manager that has no plugins."""
+ interface = IPluginInterface
+ def __init__(self):
+- self.plugins = ()
++ self._plugins = self.plugins = ()
+
+ def __iter__(self):
+ return ()
+diff -r 1b69f8a7f836 -r 7168c74ebb2e nose/plugins/multiprocess.py
+--- a/nose/plugins/multiprocess.py Fri Mar 05 15:31:57 2010 -0500
++++ b/nose/plugins/multiprocess.py Wed Jul 07 10:31:01 2010 -0400
+@@ -459,7 +459,7 @@
+ log.debug("Active plugins worker %s: %s", ix, config.plugins._plugins)
+ loader = loaderClass(config=config)
+ loader.suiteClass.suiteClass = NoSharedFixtureContextSuite
+-
++
+ def get():
+ case = testQueue.get(timeout=config.multiprocess_timeout)
+ return case
+diff -r 1b69f8a7f836 -r 7168c74ebb2e nose/result.py
+--- a/nose/result.py Fri Mar 05 15:31:57 2010 -0500
++++ b/nose/result.py Wed Jul 07 10:31:01 2010 -0400
+@@ -76,6 +76,13 @@
+ test.passed = False
+ self.printLabel('ERROR')
+
++ # override to bypass changes in 2.7
++ def getDescription(self, test):
++ if self.descriptions:
++ return test.shortDescription() or str(test)
++ else:
++ return str(test)
++
+ def printLabel(self, label, err=None):
+ # Might get patched into a streamless result
+ stream = getattr(self, 'stream', None)
+diff -r 1b69f8a7f836 -r 7168c74ebb2e nose/suite.py
+--- a/nose/suite.py Fri Mar 05 15:31:57 2010 -0500
++++ b/nose/suite.py Wed Jul 07 10:31:01 2010 -0400
+@@ -50,22 +50,30 @@
+ """Initialize the suite. tests may be an iterable or a generator
+ """
+ self._set_tests(tests)
+-
++
+ def __iter__(self):
+ return iter(self._tests)
+-
++
+ def __repr__(self):
+ return "<%s tests=generator (%s)>" % (
+ _strclass(self.__class__), id(self))
+
+ def __hash__(self):
+ return object.__hash__(self)
+-
++
+ __str__ = __repr__
+
+ def addTest(self, test):
+ self._precache.append(test)
+
++ # added to bypass run changes in 2.7's unittest
++ def run(self, result):
++ for test in self._tests:
++ if result.shouldStop:
++ break
++ test(result)
++ return result
++
+ def __nonzero__(self):
+ log.debug("tests in %s?", id(self))
+ if self._precache:
+@@ -108,7 +116,7 @@
+ "Access the tests in this suite. Access is through a "
+ "generator, so iteration may not be repeatable.")
+
+-
++
+ class ContextSuite(LazySuite):
+ """A suite with context.
+
+@@ -118,7 +126,7 @@
+ The context may be explicitly passed. If it is not, a context (or
+ nested set of contexts) will be constructed by examining the tests
+ in the suite.
+- """
++ """
+ failureException = unittest.TestCase.failureException
+ was_setup = False
+ was_torndown = False
+@@ -133,7 +141,7 @@
+ packageSetup = ('setup_package', 'setupPackage', 'setUpPackage')
+ packageTeardown = ('teardown_package', 'teardownPackage',
+ 'tearDownPackage')
+-
++
+ def __init__(self, tests=(), context=None, factory=None,
+ config=None, resultProxy=None, can_split=True):
+ log.debug("Context suite for %s (%s) (%s)", tests, context, id(self))
+@@ -159,7 +167,7 @@
+ return '%s:%s' % (repr(self), self.error_context)
+ else:
+ return repr(self)
+-
++
+ def __hash__(self):
+ return object.__hash__(self)
+
+@@ -171,7 +179,7 @@
+ """Hook for replacing error tuple output
+ """
+ return sys.exc_info()
+-
++
+ def _exc_info(self):
+ """Bottleneck to fix up IronPython string exceptions
+ """
+@@ -189,6 +197,9 @@
+ """Run tests in suite inside of suite fixtures.
+ """
+ # proxy the result for myself
++ log.debug("suite %s (%s) run called, tests: %s", id(self), self, self._tests)
++ #import pdb
++ #pdb.set_trace()
+ if self.resultProxy:
+ result, orig = self.resultProxy(result, self), result
+ else:
+@@ -252,7 +263,7 @@
+ if ctx_callback is None:
+ return fixt
+ return ctx_callback(context, fixt)
+-
++
+ def setUp(self):
+ log.debug("suite %s setUp called, tests: %s", id(self), self._tests)
+ if not self:
+@@ -339,7 +350,7 @@
+ self.teardownContext(ancestor)
+ else:
+ self.teardownContext(context)
+-
++
+ def teardownContext(self, context):
+ log.debug("%s teardown context %s", self, context)
+ if self.factory:
+@@ -413,7 +424,7 @@
+ except MixedContextError:
+ return self.makeSuite(self.mixedSuites(tests), None, **kw)
+ return self.makeSuite(tests, context, **kw)
+-
++
+ def ancestry(self, context):
+ """Return the ancestry of the context (that is, all of the
+ packages and modules containing the context), in order of
+@@ -436,7 +447,7 @@
+ raise TypeError("%s has no ancestors?" % context)
+ while ancestors:
+ log.debug(" %s ancestors %s", context, ancestors)
+- yield resolve_name('.'.join(ancestors))
++ yield resolve_name('.'.join(ancestors))
+ ancestors.pop()
+
+ def findContext(self, tests):
+@@ -508,7 +519,7 @@
+ continue
+ if test_ctx is ancestor:
+ common.append(test)
+- continue
++ continue
+ for test_ancestor in self.ancestry(test_ctx):
+ if test_ancestor is ancestor:
+ common.append(test)
+@@ -520,7 +531,7 @@
+ suite = self.makeSuite(common, ancestor)
+ tail = remain
+ return [suite] + self.mixedSuites(tail)
+-
++
+ def wrapTests(self, tests):
+ log.debug("wrap %s", tests)
+ if callable(tests) or isinstance(tests, unittest.TestSuite):
+@@ -557,7 +568,7 @@
+ """Wraps suite and calls final function after suite has
+ executed. Used to call final functions in cases (like running in
+ the standard test runner) where test running is not under nose's
+- control.
++ control.
+ """
+ def __init__(self, suite, finalize):
+ self.suite = suite
+@@ -566,6 +577,10 @@
+ def __call__(self, *arg, **kw):
+ return self.run(*arg, **kw)
+
++ # 2.7 compat
++ def __iter__(self):
++ return iter(self.suite)
++
+ def run(self, *arg, **kw):
+ try:
+ return self.suite(*arg, **kw)
+diff -r 1b69f8a7f836 -r 7168c74ebb2e unit_tests/test_logcapture_plugin.py
+--- a/unit_tests/test_logcapture_plugin.py Fri Mar 05 15:31:57 2010 -0500
++++ b/unit_tests/test_logcapture_plugin.py Wed Jul 07 10:31:01 2010 -0400
+@@ -1,4 +1,3 @@
+-
+ import sys
+ from optparse import OptionParser
+ from nose.config import Config
+@@ -8,6 +7,11 @@
+ from logging import StreamHandler
+ import unittest
+
++if sys.version_info >= (2, 7):
++ py27 = True
++else:
++ py27 = False
++
+ class TestLogCapturePlugin(object):
+
+ def test_enabled_by_default(self):
+@@ -81,25 +85,30 @@
+ options, args = parser.parse_args(['--logging-clear-handlers'])
+ c.configure(options, Config())
+ eq_(c.clear, True)
+-
+- def mktest():
++
++ def mktest():
+ class TC(unittest.TestCase):
+ def runTest(self):
+ pass
+ test = TC()
+ return test
+-
++
+ logging.getLogger().addHandler(StreamHandler(sys.stdout))
+ log = logging.getLogger("dummy")
+ log.addHandler(StreamHandler(sys.stdout))
+-
++
+ c.start()
+ c.beforeTest(mktest())
+ c.end()
+-
+- eq_([str(c.__class__) for c in logging.getLogger().handlers],
+- ['nose.plugins.logcapture.MyMemoryHandler'])
+- eq_([str(c.__class__) for c in logging.getLogger("dummy").handlers],
++
++
++ if py27:
++ expect = [""]
++ else:
++ expect = ['nose.plugins.logcapture.MyMemoryHandler']
++ eq_([str(c.__class__) for c in logging.getLogger().handlers],
++ expect)
++ eq_([str(c.__class__) for c in logging.getLogger("dummy").handlers],
+ [])
+
+ def test_custom_formatter(self):
+@@ -112,7 +121,7 @@
+ records = c.formatLogRecords()
+ eq_(1, len(records))
+ eq_("++Hello++", records[0])
+-
++
+ def test_logging_filter(self):
+ env = {'NOSE_LOGFILTER': 'foo,bar'}
+ c = LogCapture()
diff --git a/python-nose.spec b/python-nose.spec
index 527c9b3..1018124 100644
--- a/python-nose.spec
+++ b/python-nose.spec
@@ -5,9 +5,12 @@
%global upstream_name nose
+# Enable building without docs to avoid a circular dependency between this and python-sphinx
+%global with_docs 0
+
Name: python-nose
Version: 0.11.3
-Release: 4%{?dist}
+Release: 5%{?dist}
Summary: A discovery-based unittest extension for Python
Group: Development/Languages
@@ -16,6 +19,12 @@ URL: http://somethingaboutorange.com/mrl/projects/nose/
Source0: http://somethingaboutorange.com/mrl/projects/nose/nose-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+# 2.7 compatibility fixes
+# Extracted from upstream scm using:
+# hg diff -r 708 -r 711
+# and eliminating the hunk relating to test.sh
+Patch0: python-nose-r708-through-r111-py27-fixes.patch
+
BuildArch: noarch
BuildRequires: python2-devel
%if 0%{?fedora} && 0%{?fedora} < 13
@@ -24,6 +33,7 @@ BuildRequires: python-setuptools-devel
BuildRequires: python-setuptools
%endif
BuildRequires: dos2unix
+BuildRequires: python-coverage
Requires: python-setuptools
%description
@@ -42,6 +52,7 @@ plugins. Plugins included with nose provide support for doctest, code
coverage and profiling, flexible attribute-based test selection,
output capture and more.
+%if 0%{?with_docs}
%package docs
Summary: Nose Documentation
Group: Documentation
@@ -49,10 +60,11 @@ BuildRequires: python-sphinx
%description docs
Documentation for Nose
-
+%endif # with_docs
%prep
%setup -q -n %{upstream_name}-%{version}
+%patch0 -p1
dos2unix examples/attrib_plugin.py
@@ -65,13 +77,15 @@ rm -rf %{buildroot}
%{__python} setup.py install --skip-build --root %{buildroot} \
--install-data=%{_datadir}
+%if 0%{?with_docs}
pushd doc
make html
-rm -rf .build/html/.buildinfo .build/html/_sources
+]rm -rf .build/html/.buildinfo .build/html/_sources
mv .build/html ..
rm -rf .build
popd
cp -a doc reST
+%endif # with_docs
%check
@@ -84,17 +98,26 @@ rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
-%doc AUTHORS CHANGELOG html lgpl.txt NEWS README.txt
+%doc AUTHORS CHANGELOG lgpl.txt NEWS README.txt
%{_bindir}/nosetests
%{_bindir}/nosetests-%{python_version}
%{_mandir}/man1/nosetests.1.gz
%{python_sitelib}/nose*
+%if 0%{?with_docs}
%files docs
%defattr(-,root,root,-)
%doc html reST examples
+%endif # with_docs
%changelog
+* Wed Jul 21 2010 David Malcolm - 0.11.3-5
+- add support for building without docs, to avoid a circular build-time
+dependency between this and python-sphinx; disable docs subpackage for now
+- add (apparently) missing BR on python-coverage (appears to be needed
+for %%check)
+- cherrypick upstream compatibility fixes for 2.7
+
* Wed Jul 21 2010 David Malcolm - 0.11.3-4
- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild