Hello community,

here is the log from the commit of package python3-testtools for 
openSUSE:Factory checked in at 2015-03-19 21:12:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-testtools (Old)
 and      /work/SRC/openSUSE:Factory/.python3-testtools.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python3-testtools"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python3-testtools/python3-testtools.changes      
2015-02-18 12:08:54.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python3-testtools.new/python3-testtools.changes 
2015-03-19 21:12:33.000000000 +0100
@@ -1,0 +2,35 @@
+Wed Mar 11 05:36:08 UTC 2015 - [email protected]
+
+- specfile:
+  * added traceback2 requirement
+
+- update to version 1.7.1:
+  * Building a wheel on Python 3 was missing "_compat2x.py" needed
+    for Python2.  This was a side effect of the fix to bug #941958,
+    where we fixed a cosmetic error. (Robert Collins, #1430534)
+  * During reporting in "TextTestResult" now always uses "ceil"
+    rather than depending on the undefined rounding behaviour in
+    string formatting.  (Robert Collins)
+
+- changes from version 1.7.0:
+  * Empty attachments to tests were triggering a file payload of None
+    in the "ExtendedToStreamDecorator" code, which caused multiple
+    copies of attachments that had been output prior to the empty one.
+    (Robert Collins, #1378609)
+
+- changes from version 1.6.1:
+  * Fix installing when "extras" is not already installed. Our guards
+    for the absence of unittest2 were not sufficient.  (Robert
+    Collins, #1430076)
+
+- changes from version 1.6.0:
+  * "testtools.run" now accepts "--locals" to show local variables in
+    tracebacks, which can be a significant aid in debugging. In doing
+    so we've removed the code reimplementing linecache and traceback
+    by using the new traceback2 and linecache2 packages.  (Robert
+    Collins, github #111)
+  * "testtools" now depends on "unittest2" 1.0.0 which brings in a
+    dependency on "traceback2" and via it "linecache2". (Robert
+    Collins)
+
+-------------------------------------------------------------------

Old:
----
  testtools-1.5.0.tar.gz

New:
----
  testtools-1.7.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python3-testtools.spec ++++++
--- /var/tmp/diff_new_pack.Shp5tS/_old  2015-03-19 21:12:34.000000000 +0100
+++ /var/tmp/diff_new_pack.Shp5tS/_new  2015-03-19 21:12:34.000000000 +0100
@@ -20,7 +20,7 @@
 # enable testing with a build conditional (off by default):
 
 Name:           python3-testtools
-Version:        1.5.0
+Version:        1.7.1
 Release:        0
 Summary:        Extensions to the Python Standard Library Unit Testing 
Framework
 License:        MIT
@@ -34,10 +34,12 @@
 # Test requirements:
 BuildRequires:  python3-extras
 BuildRequires:  python3-python-mimeparse
+BuildRequires:  python3-traceback2
 BuildRequires:  python3-unittest2
-Requires:       python3-unittest2
 Requires:       python3-extras
 Requires:       python3-python-mimeparse
+Requires:       python3-traceback2
+Requires:       python3-unittest2
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildArch:      noarch
 

++++++ testtools-1.5.0.tar.gz -> testtools-1.7.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/NEWS new/testtools-1.7.1/NEWS
--- old/testtools-1.5.0/NEWS    2014-11-21 00:20:13.000000000 +0100
+++ new/testtools-1.7.1/NEWS    2015-03-11 02:17:37.000000000 +0100
@@ -7,6 +7,59 @@
 NEXT
 ~~~~
 
+1.7.1
+~~~~~
+
+Improvements
+------------
+
+* Building a wheel on Python 3 was missing ``_compat2x.py`` needed for Python2.
+  This was a side effect of the fix to bug #941958, where we fixed a cosmetic
+  error. (Robert Collins, #1430534)
+
+* During reporting in ``TextTestResult`` now always uses ``ceil`` rather than
+  depending on the undefined rounding behaviour in string formatting.
+  (Robert Collins)
+
+1.7.0
+~~~~~
+
+Improvements
+------------
+
+* Empty attachments to tests were triggering a file payload of None in the
+  ``ExtendedToStreamDecorator`` code, which caused multiple copies of
+  attachments that had been output prior to the empty one.
+  (Robert Collins, #1378609)
+
+1.6.1
+~~~~~
+
+Changes
+-------
+
+* Fix installing when ``extras`` is not already installed. Our guards
+  for the absence of unittest2 were not sufficient.
+  (Robert Collins, #1430076)
+
+1.6.0
+~~~~~
+
+Improvements
+------------
+
+* ``testtools.run`` now accepts ``--locals`` to show local variables
+  in tracebacks, which can be a significant aid in debugging. In doing
+  so we've removed the code reimplementing linecache and traceback by
+  using the new traceback2 and linecache2 packages.
+  (Robert Collins, github #111)
+
+Changes
+-------
+
+* ``testtools`` now depends on ``unittest2`` 1.0.0 which brings in a dependency
+  on ``traceback2`` and via it ``linecache2``. (Robert Collins)
+
 1.5.0
 ~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/PKG-INFO new/testtools-1.7.1/PKG-INFO
--- old/testtools-1.5.0/PKG-INFO        2014-11-21 00:20:56.000000000 +0100
+++ new/testtools-1.7.1/PKG-INFO        2015-03-11 02:18:22.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: testtools
-Version: 1.5.0
+Version: 1.7.1
 Summary: Extensions to the Python standard library unit testing framework
 Home-page: https://github.com/testing-cabal/testtools
 Author: Jonathan M. Lange
Files old/testtools-1.5.0/doc/.hacking.rst.swp and 
new/testtools-1.7.1/doc/.hacking.rst.swp differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/doc/for-framework-folk.rst 
new/testtools-1.7.1/doc/for-framework-folk.rst
--- old/testtools-1.5.0/doc/for-framework-folk.rst      2014-09-12 
00:21:19.000000000 +0200
+++ new/testtools-1.7.1/doc/for-framework-folk.rst      2015-03-10 
00:19:47.000000000 +0100
@@ -92,6 +92,13 @@
 This is useful when you want to cause a test to fail, but don't want to
 prevent the remainder of the test code from being executed.
 
+Exception formatting
+--------------------
+
+Testtools ``TestCase`` instances format their own exceptions. The attribute
+``__testtools_tb_locals__`` controls whether to include local variables in the
+formatted exceptions.
+
 Test placeholders
 =================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/doc/for-framework-folk.rst~ 
new/testtools-1.7.1/doc/for-framework-folk.rst~
--- old/testtools-1.5.0/doc/for-framework-folk.rst~     2014-09-02 
02:16:59.000000000 +0200
+++ new/testtools-1.7.1/doc/for-framework-folk.rst~     2015-03-09 
10:26:26.000000000 +0100
@@ -50,9 +50,9 @@
 change everything about how the test executes.
 
 To work with ``testtools.TestCase``, a ``RunTest`` must have a factory that
-takes a test and an optional list of exception handlers.  Instances returned
-by the factory must have a ``run()`` method that takes an optional 
``TestResult``
-object.
+takes a test and an optional list of exception handlers and an optional
+last_resort handler.  Instances returned by the factory must have a ``run()``
+method that takes an optional ``TestResult`` object.
 
 The default is ``testtools.runtest.RunTest``, which calls ``setUp``, the test
 method, ``tearDown`` and clean ups (see :ref:`addCleanup`) in the normal, 
vanilla
@@ -92,6 +92,13 @@
 This is useful when you want to cause a test to fail, but don't want to
 prevent the remainder of the test code from being executed.
 
+Exception formatting
+--------------------
+
+Testtools ``TestCase`` do their own formatting of exceptions. The attribute
+``__testtools_tb_locals__`` controls whether to include local variables in the
+formatted exceptions.
+
 Test placeholders
 =================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/doc/for-test-authors.rst 
new/testtools-1.7.1/doc/for-test-authors.rst
--- old/testtools-1.5.0/doc/for-test-authors.rst        2014-08-28 
11:34:09.000000000 +0200
+++ new/testtools-1.7.1/doc/for-test-authors.rst        2015-03-10 
00:19:47.000000000 +0100
@@ -72,10 +72,12 @@
 
   $ python -m testtools.run discover packagecontainingtests
 
-For more information see the Python 2.7 unittest documentation, or::
+For more information see the Python unittest documentation, and::
 
     python -m testtools.run --help
 
+which describes the options available to ``testtools.run``.
+
 As your testing needs grow and evolve, you will probably want to use a more
 sophisticated test runner.  There are many of these for Python, and almost all
 of them will happily run testtools tests.  In particular:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/doc/for-test-authors.rst~ 
new/testtools-1.7.1/doc/for-test-authors.rst~
--- old/testtools-1.5.0/doc/for-test-authors.rst~       2013-11-28 
08:54:45.000000000 +0100
+++ new/testtools-1.7.1/doc/for-test-authors.rst~       2014-08-28 
11:34:09.000000000 +0200
@@ -11,7 +11,7 @@
 If you are a test author of an unusually large or unusually unusual test
 suite, you might be interested in :doc:`for-framework-folk`.
 
-You might also be interested in the `testtools API docs`_.
+You might also be interested in the :doc:`testtools API docs </api>`.
 
 
 Introduction
@@ -288,6 +288,60 @@
       self.assertNotEqual(result, 50)
 
 
+``assert_that`` Function
+------------------------
+
+In addition to ``self.assertThat``, testtools also provides the ``assert_that``
+function in ``testtools.assertions`` This behaves like the method version 
does::
+
+    class TestSquare(TestCase):
+
+        def test_square():
+            result = square(7)
+            assert_that(result, Equals(49))
+
+        def test_square_silly():
+            result = square(7)
+            assert_that(result, Not(Equals(50)))
+
+
+Delayed Assertions
+~~~~~~~~~~~~~~~~~~
+
+A failure in the ``self.assertThat`` method will immediately fail the test: No
+more test code will be run after the assertion failure.
+
+The ``expectThat`` method behaves the same as ``assertThat`` with one
+exception: when failing the test it does so at the end of the test code rather
+than when the mismatch is detected. For example::
+
+  import subprocess
+
+  from testtools import TestCase
+  from testtools.matchers import Equals
+
+
+  class SomeProcessTests(TestCase):
+
+      def test_process_output(self):
+          process = subprocess.Popen(
+            ["my-app", "/some/path"],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE
+          )
+
+          stdout, stderrr = process.communicate()
+
+          self.expectThat(process.returncode, Equals(0))
+          self.expectThat(stdout, Equals("Expected Output"))
+          self.expectThat(stderr, Equals(""))
+
+In this example, should the ``expectThat`` call fail, the failure will be
+recorded in the test result, but the test will continue as normal. If all
+three assertions fail, the test result will have three failures recorded, and
+the failure details for each failed assertion will be attached to the test
+result.
+
 Stock matchers
 --------------
 
@@ -408,7 +462,7 @@
       except RuntimeError:
           exc_info = sys.exc_info()
       self.assertThat(exc_info, MatchesException(RuntimeError))
-      self.assertThat(exc_info, MatchesException(RuntimeError('bar'))
+      self.assertThat(exc_info, MatchesException(RuntimeError('bar')))
 
 Most of the time, you will want to uses `The raises helper`_ instead.
 
@@ -598,7 +652,7 @@
   def test_annotate_example(self):
       result = 43
       self.assertThat(
-          result, Annotate("Not the answer to the Question!", Equals(42))
+          result, Annotate("Not the answer to the Question!", Equals(42)))
 
 Since the annotation is only ever displayed when there is a mismatch
 (e.g. when ``result`` does not equal 42), it's a good idea to phrase the note
@@ -811,7 +865,7 @@
           divisible, '{0} is not divisible by {1}')
       self.assertThat(7, IsDivisibleBy(1))
       self.assertThat(7, IsDivisibleBy(7))
-      self.assertThat(7, IsDivisibleBy(2)))
+      self.assertThat(7, IsDivisibleBy(2))
       # This will fail.
 
 Which will produce the error message::
@@ -1246,7 +1300,9 @@
 force_failure
 -------------
 
-Setting the ``testtools.TestCase.force_failure`` instance variable to ``True`` 
will cause the test to be marked as a failure, but won't stop the test code 
from running (see :ref:`force_failure`).
+Setting the ``testtools.TestCase.force_failure`` instance variable to ``True``
+will cause the test to be marked as a failure, but won't stop the test code
+from running (see :ref:`force_failure`).
 
 
 Test helpers
@@ -1335,14 +1391,14 @@
 id and can be used when filtering tests by id. (e.g. via ``--load-list``)::
 
   from testtools.testcase import attr, WithAttributes
-  
+
   class AnnotatedTests(WithAttributes, TestCase):
 
       @attr('simple')
       def test_one(self):
           pass
-      
-      @attr('more', 'than', 'one)
+
+      @attr('more', 'than', 'one')
       def test_two(self):
           pass
 
@@ -1424,7 +1480,6 @@
 .. _doctest: http://docs.python.org/library/doctest.html
 .. _Deferred: http://twistedmatrix.com/documents/current/core/howto/defer.html
 .. _discover: http://pypi.python.org/pypi/discover
-.. _`testtools API docs`: http://mumak.net/testtools/apidocs/
 .. _Distutils: http://docs.python.org/library/distutils.html
 .. _`setup configuration`: http://docs.python.org/distutils/configfile.html
 .. _broken: http://chipaca.com/post/3210673069/hasattr-17-less-harmful
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/doc/hacking.rst~ 
new/testtools-1.7.1/doc/hacking.rst~
--- old/testtools-1.5.0/doc/hacking.rst~        2014-08-29 03:45:37.000000000 
+0200
+++ new/testtools-1.7.1/doc/hacking.rst~        2014-08-31 12:10:39.000000000 
+0200
@@ -130,6 +130,18 @@
 
 Changes which all users should be made aware of should be documented in NEWS.
 
+We are now in full backwards compatibility mode - no more releases < 1.0.0, 
and 
+breaking compatibility will require consensus on the testtools-dev mailing 
list.
+Exactly what constitutes a backwards incompatible change is vague, but 
coarsely:
+
+* adding required arguments or required calls to something that used to work
+* removing keyword or position arguments, removing methods, functions or 
modules
+* changing behaviour someone may have reasonably depended on
+
+Some things are not compatibility issues:
+
+* changes to _ prefixed methods, functions, modules, packages.
+
 
 NEWS management
 ---------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/setup.py new/testtools-1.7.1/setup.py
--- old/testtools-1.5.0/setup.py        2014-11-15 22:33:08.000000000 +0100
+++ new/testtools-1.7.1/setup.py        2015-03-10 22:19:19.000000000 +0100
@@ -2,10 +2,8 @@
 """Distutils installer for testtools."""
 
 from setuptools import setup
-from distutils.command.build_py import build_py
 import email
 import os
-import sys
 
 import testtools
 cmd_class = {}
@@ -13,14 +11,6 @@
     cmd_class['test'] = testtools.TestCommand
 
 
-class testtools_build_py(build_py):
-    def build_module(self, module, module_file, package):
-        if sys.version_info >= (3,) and module == '_compat2x':
-            return
-        return build_py.build_module(self, module, module_file, package)
-cmd_class['build_py'] = testtools_build_py
-
-
 def get_version_from_pkg_info():
     """Get the version from PKG-INFO file if we can."""
     pkg_info_path = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
@@ -63,7 +53,8 @@
     # 'mimeparse' has not been uploaded by the maintainer with Python3 compat
     # but someone kindly uploaded a fixed version as 'python-mimeparse'.
     'python-mimeparse',
-    'unittest2>=0.8.0',
+    'unittest2>=1.0.0',
+    'traceback2',
     ]
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/__init__.py 
new/testtools-1.7.1/testtools/__init__.py
--- old/testtools-1.5.0/testtools/__init__.py   2014-11-21 00:20:00.000000000 
+0100
+++ new/testtools-1.7.1/testtools/__init__.py   2015-03-11 02:17:24.000000000 
+0100
@@ -122,4 +122,4 @@
 # If the releaselevel is 'final', then the tarball will be major.minor.micro.
 # Otherwise it is major.minor.micro~$(revno).
 
-__version__ = (1, 5, 0, 'final', 0)
+__version__ = (1, 7, 1, 'final', 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/compat.py 
new/testtools-1.7.1/testtools/compat.py
--- old/testtools-1.5.0/testtools/compat.py     2014-11-15 10:08:40.000000000 
+0100
+++ new/testtools-1.7.1/testtools/compat.py     2015-03-10 22:19:19.000000000 
+0100
@@ -18,7 +18,6 @@
 
 import codecs
 import io
-import linecache
 import locale
 import os
 import re
@@ -26,14 +25,16 @@
 import traceback
 import unicodedata
 
-from extras import try_imports
+from extras import try_import, try_imports
 
 BytesIO = try_imports(['StringIO.StringIO', 'io.BytesIO'])
 StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])
+# To let setup.py work, make this a conditional import.
+linecache = try_import('linecache2')
 
 try:
     from testtools import _compat2x as _compat
-except (SyntaxError, ImportError):
+except SyntaxError:
     from testtools import _compat3x as _compat
 
 reraise = _compat.reraise
@@ -209,61 +210,6 @@
         except AttributeError:
             pass
     return writer(stream, "replace")
-
-
-# The default source encoding is actually "iso-8859-1" until Python 2.5 but
-# using non-ascii causes a deprecation warning in 2.4 and it's cleaner to
-# treat all versions the same way
-_default_source_encoding = "ascii"
-
-# Pattern specified in <http://www.python.org/dev/peps/pep-0263/>
-_cookie_search=re.compile("coding[:=]\s*([-\w.]+)").search
-
-def _detect_encoding(lines):
-    """Get the encoding of a Python source file from a list of lines as bytes
-
-    This function does less than tokenize.detect_encoding added in Python 3 as
-    it does not attempt to raise a SyntaxError when the interpreter would, it
-    just wants the encoding of a source file Python has already compiled and
-    determined is valid.
-    """
-    if not lines:
-        return _default_source_encoding
-    if lines[0].startswith("\xef\xbb\xbf"):
-        # Source starting with UTF-8 BOM is either UTF-8 or a SyntaxError
-        return "utf-8"
-    # Only the first two lines of the source file are examined
-    magic = _cookie_search("".join(lines[:2]))
-    if magic is None:
-        return _default_source_encoding
-    encoding = magic.group(1)
-    try:
-        codecs.lookup(encoding)
-    except LookupError:
-        # Some codecs raise something other than LookupError if they don't
-        # support the given error handler, but not the text ones that could
-        # actually be used for Python source code
-        return _default_source_encoding
-    return encoding
-
-
-class _EncodingTuple(tuple):
-    """A tuple type that can have an encoding attribute smuggled on"""
-
-
-def _get_source_encoding(filename):
-    """Detect, cache and return the encoding of Python source at filename"""
-    try:
-        return linecache.cache[filename].encoding
-    except (AttributeError, KeyError):
-        encoding = _detect_encoding(linecache.getlines(filename))
-        if filename in linecache.cache:
-            newtuple = _EncodingTuple(linecache.cache[filename])
-            newtuple.encoding = encoding
-            linecache.cache[filename] = newtuple
-        return encoding
-
-
 def _get_exception_encoding():
     """Return the encoding we expect messages from the OS to be encoded in"""
     if os.name == "nt":
@@ -276,110 +222,3 @@
     return locale.getlocale(locale.LC_MESSAGES)[1] or "ascii"
 
 
-def _exception_to_text(evalue):
-    """Try hard to get a sensible text value out of an exception instance"""
-    try:
-        return unicode(evalue)
-    except KeyboardInterrupt:
-        raise
-    except:
-        # Apparently this is what traceback._some_str does. Sigh - RBC 20100623
-        pass
-    try:
-        return str(evalue).decode(_get_exception_encoding(), "replace")
-    except KeyboardInterrupt:
-        raise
-    except:
-        # Apparently this is what traceback._some_str does. Sigh - RBC 20100623
-        pass
-    # Okay, out of ideas, let higher level handle it
-    return None
-
-
-def _format_stack_list(stack_lines):
-    """Format 'stack_lines' and return a list of unicode strings.
-
-    :param stack_lines: A list of filename, lineno, name, and line variables,
-        probably obtained by calling traceback.extract_tb or
-        traceback.extract_stack.
-    """
-    fs_enc = sys.getfilesystemencoding()
-    extracted_list = []
-    for filename, lineno, name, line in stack_lines:
-            extracted_list.append((
-                filename.decode(fs_enc, "replace"),
-                lineno,
-                name.decode("ascii", "replace"),
-                line and line.decode(
-                    _get_source_encoding(filename), "replace")))
-    return traceback.format_list(extracted_list)
-
-
-def _format_exception_only(eclass, evalue):
-    """Format the excption part of a traceback.
-
-    :param eclass: The type of the exception being formatted.
-    :param evalue: The exception instance.
-    :returns: A list of unicode strings.
-    """
-    list = []
-    if evalue is None:
-        # Is a (deprecated) string exception
-        list.append((eclass + "\n").decode("ascii", "replace"))
-        return list
-    if isinstance(evalue, SyntaxError):
-        # Avoid duplicating the special formatting for SyntaxError here,
-        # instead create a new instance with unicode filename and line
-        # Potentially gives duff spacing, but that's a pre-existing issue
-        try:
-            msg, (filename, lineno, offset, line) = evalue
-        except (TypeError, ValueError):
-            pass # Strange exception instance, fall through to generic code
-        else:
-            # Errors during parsing give the line from buffer encoded as
-            # latin-1 or utf-8 or the encoding of the file depending on the
-            # coding and whether the patch for issue #1031213 is applied, so
-            # give up on trying to decode it and just read the file again
-            if line:
-                bytestr = linecache.getline(filename, lineno)
-                if bytestr:
-                    if lineno == 1 and bytestr.startswith("\xef\xbb\xbf"):
-                        bytestr = bytestr[3:]
-                    line = bytestr.decode(
-                        _get_source_encoding(filename), "replace")
-                    del linecache.cache[filename]
-                else:
-                    line = line.decode("ascii", "replace")
-            if filename:
-                fs_enc = sys.getfilesystemencoding()
-                filename = filename.decode(fs_enc, "replace")
-            evalue = eclass(msg, (filename, lineno, offset, line))
-            list.extend(traceback.format_exception_only(eclass, evalue))
-            return list
-    sclass = eclass.__name__
-    svalue = _exception_to_text(evalue)
-    if svalue:
-        list.append("%s: %s\n" % (sclass, svalue))
-    elif svalue is None:
-        # GZ 2010-05-24: Not a great fallback message, but keep for the moment
-        list.append(_u("%s: <unprintable %s object>\n" % (sclass, sclass)))
-    else:
-        list.append(_u("%s\n" % sclass))
-    return list
-
-
-_TB_HEADER = _u('Traceback (most recent call last):\n')
-
-
-def _format_exc_info(eclass, evalue, tb, limit=None):
-    """Format a stack trace and the exception information as unicode
-
-    Compatibility function for Python 2 which ensures each component of a
-    traceback is correctly decoded according to its origins.
-
-    Based on traceback.format_exception and related functions.
-    """
-    return [_TB_HEADER] \
-        + _format_stack_list(traceback.extract_tb(tb, limit)) \
-        + _format_exception_only(eclass, evalue)
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/content.py 
new/testtools-1.7.1/testtools/content.py
--- old/testtools-1.5.0/testtools/content.py    2014-10-20 04:16:48.000000000 
+0200
+++ new/testtools-1.7.1/testtools/content.py    2015-03-10 00:19:47.000000000 
+0100
@@ -17,15 +17,13 @@
 import json
 import os
 import sys
-import traceback
 
 from extras import try_import
+# To let setup.py work, make this a conditional import.
+traceback = try_import('traceback2')
 
 from testtools.compat import (
     _b,
-    _format_exception_only,
-    _format_stack_list,
-    _TB_HEADER,
     _u,
     istext,
     str_is_unicode,
@@ -163,61 +161,52 @@
     def _stack_lines_to_unicode(self, stack_lines):
         """Converts a list of pre-processed stack lines into a unicode string.
         """
+        msg_lines = traceback.format_list(stack_lines)
+        return _u('').join(msg_lines)
 
-        # testtools customization. When str is unicode (e.g. IronPython,
-        # Python 3), traceback.format_exception returns unicode. For Python 2,
-        # it returns bytes. We need to guarantee unicode.
-        if str_is_unicode:
-            format_stack_lines = traceback.format_list
-        else:
-            format_stack_lines = _format_stack_list
 
-        msg_lines = format_stack_lines(stack_lines)
-
-        return ''.join(msg_lines)
-
-
-def TracebackContent(err, test):
+class TracebackContent(Content):
     """Content object for tracebacks.
 
     This adapts an exc_info tuple to the 'Content' interface.
     'text/x-traceback;language=python' is used for the mime type, in order to
     provide room for other languages to format their tracebacks differently.
     """
-    if err is None:
-        raise ValueError("err may not be None")
 
-    exctype, value, tb = err
-    # Skip test runner traceback levels
-    if StackLinesContent.HIDE_INTERNAL_STACK:
-        while tb and '__unittest' in tb.tb_frame.f_globals:
-            tb = tb.tb_next
-
-    # testtools customization. When str is unicode (e.g. IronPython,
-    # Python 3), traceback.format_exception_only returns unicode. For Python 2,
-    # it returns bytes. We need to guarantee unicode.
-    if str_is_unicode:
-        format_exception_only = traceback.format_exception_only
-    else:
-        format_exception_only = _format_exception_only
-
-    limit = None
-    # Disabled due to https://bugs.launchpad.net/testtools/+bug/1188420
-    if (False
-        and StackLinesContent.HIDE_INTERNAL_STACK
-        and test.failureException
-        and isinstance(value, test.failureException)):
-        # Skip assert*() traceback levels
-        limit = 0
-        while tb and not self._is_relevant_tb_level(tb):
-            limit += 1
-            tb = tb.tb_next
-
-    prefix = _TB_HEADER
-    stack_lines = traceback.extract_tb(tb, limit)
-    postfix = ''.join(format_exception_only(exctype, value))
+    def __init__(self, err, test, capture_locals=False):
+        """Create a TracebackContent for ``err``.
 
-    return StackLinesContent(stack_lines, prefix, postfix)
+        :param err: An exc_info error tuple.
+        :param test: A test object used to obtain failureException.
+        :param capture_locals: If true, show locals in the traceback.
+        """
+        if err is None:
+            raise ValueError("err may not be None")
+
+        exctype, value, tb = err
+        # Skip test runner traceback levels
+        if StackLinesContent.HIDE_INTERNAL_STACK:
+            while tb and '__unittest' in tb.tb_frame.f_globals:
+                tb = tb.tb_next
+
+        limit = None
+        # Disabled due to https://bugs.launchpad.net/testtools/+bug/1188420
+        if (False
+            and StackLinesContent.HIDE_INTERNAL_STACK
+            and test.failureException
+            and isinstance(value, test.failureException)):
+            # Skip assert*() traceback levels
+            limit = 0
+            while tb and not self._is_relevant_tb_level(tb):
+                limit += 1
+                tb = tb.tb_next
+
+        stack_lines = list(traceback.TracebackException(exctype, value, tb,
+            limit=limit, capture_locals=capture_locals).format())
+        content_type = ContentType('text', 'x-traceback',
+            {"language": "python", "charset": "utf8"})
+        super(TracebackContent, self).__init__(
+            content_type, lambda: [x.encode('utf8') for x in stack_lines])
 
 
 def StacktraceContent(prefix_content="", postfix_content=""):
@@ -232,22 +221,20 @@
     :param prefix_content: A unicode string to add before the stack lines.
     :param postfix_content: A unicode string to add after the stack lines.
     """
-    stack = inspect.stack()[1:]
-
-    if StackLinesContent.HIDE_INTERNAL_STACK:
-        limit = 1
-        while limit < len(stack) and '__unittest' not in 
stack[limit][0].f_globals:
-            limit += 1
-    else:
-        limit = -1
-
-    frames_only = [line[0] for line in stack[:limit]]
-    processed_stack = [ ]
-    for frame in reversed(frames_only):
-        filename, line, function, context, _ = inspect.getframeinfo(frame)
-        context = ''.join(context)
-        processed_stack.append((filename, line, function, context))
-    return StackLinesContent(processed_stack, prefix_content, postfix_content)
+    stack = traceback.walk_stack(None)
+    def filter_stack(stack):
+        # Discard the filter_stack frame.
+        next(stack)
+        # Discard the StacktraceContent frame.
+        next(stack)
+        for f, f_lineno in stack:
+            if StackLinesContent.HIDE_INTERNAL_STACK:
+                if '__unittest' in f.f_globals:
+                    return
+                yield f, f_lineno
+    extract = traceback.StackSummary.extract(filter_stack(stack))
+    extract.reverse()
+    return StackLinesContent(extract, prefix_content, postfix_content)
 
 
 def json_content(json_data):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/run.py 
new/testtools-1.7.1/testtools/run.py
--- old/testtools-1.5.0/testtools/run.py        2014-11-18 22:55:29.000000000 
+0100
+++ new/testtools-1.7.1/testtools/run.py        2015-03-10 01:17:43.000000000 
+0100
@@ -11,10 +11,11 @@
 import argparse
 from functools import partial
 import os.path
-import unittest2 as unittest
 import sys
 
-from extras import safe_hasattr
+from extras import safe_hasattr, try_imports
+# To let setup.py work, make this a conditional import.
+unittest = try_imports(['unittest2', 'unittest'])
 
 from testtools import TextTestResult, testcase
 from testtools.compat import classtypes, istext, unicode_output_stream
@@ -24,7 +25,10 @@
 defaultTestLoader = unittest.defaultTestLoader
 defaultTestLoaderCls = unittest.TestLoader
 have_discover = True
-discover_impl = unittest.loader
+# This shouldn't really be public - its legacy.  Try to set it if we can, and
+# if we can't (during installs before unittest2 is installed) just stub it out
+# to None.
+discover_impl = getattr(unittest, 'loader', None)
 
 # Kept for API compatibility, but no longer used.
 BUFFEROUTPUT = ""
@@ -67,18 +71,20 @@
     """ A thunk object to support unittest.TestProgram."""
 
     def __init__(self, verbosity=None, failfast=None, buffer=None,
-        stdout=None):
+        stdout=None, tb_locals=False, **kwargs):
         """Create a TestToolsTestRunner.
 
         :param verbosity: Ignored.
         :param failfast: Stop running tests at the first failure.
         :param buffer: Ignored.
         :param stdout: Stream to use for stdout.
+        :param tb_locals: If True include local variables in tracebacks.
         """
         self.failfast = failfast
         if stdout is None:
             stdout = sys.stdout
         self.stdout = stdout
+        self.tb_locals = tb_locals
 
     def list(self, test, loader):
         """List the tests that would be run if test() was run."""
@@ -94,7 +100,8 @@
     def run(self, test):
         "Run the given test case or test suite."
         result = TextTestResult(
-            unicode_output_stream(self.stdout), failfast=self.failfast)
+            unicode_output_stream(self.stdout), failfast=self.failfast,
+            tb_locals=self.tb_locals)
         result.startTestRun()
         try:
             return test.run(result)
@@ -127,7 +134,7 @@
     def __init__(self, module=__name__, defaultTest=None, argv=None,
                     testRunner=None, testLoader=defaultTestLoader,
                     exit=True, verbosity=1, failfast=None, catchbreak=None,
-                    buffer=None, stdout=None):
+                    buffer=None, stdout=None, tb_locals=False):
         if module == __name__:
             self.module = None
         elif istext(module):
@@ -147,6 +154,7 @@
         self.catchbreak = catchbreak
         self.verbosity = verbosity
         self.buffer = buffer
+        self.tb_locals = tb_locals
         self.defaultTest = defaultTest
         # XXX: Local edit (see http://bugs.python.org/issue22860)
         self.listtests = False
@@ -219,10 +227,18 @@
         if self.testRunner is None:
             self.testRunner = TestToolsTestRunner
         try:
-            testRunner = self.testRunner(verbosity=self.verbosity,
-                                         failfast=self.failfast,
-                                         buffer=self.buffer,
-                                         stdout=self.stdout)
+            try:
+                testRunner = self.testRunner(verbosity=self.verbosity,
+                                             failfast=self.failfast,
+                                             buffer=self.buffer,
+                                             stdout=self.stdout,
+                                             tb_locals=self.tb_locals)
+            except TypeError:
+                # didn't accept the tb_locals parameter
+                testRunner = self.testRunner(verbosity=self.verbosity,
+                                             failfast=self.failfast,
+                                             buffer=self.buffer,
+                                             stdout=self.stdout)
         except TypeError:
             # didn't accept the verbosity, buffer, failfast or stdout arguments
             # Try with the prior contract
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/runtest.py 
new/testtools-1.7.1/testtools/runtest.py
--- old/testtools-1.5.0/testtools/runtest.py    2014-09-02 13:20:23.000000000 
+0200
+++ new/testtools-1.7.1/testtools/runtest.py    2015-03-10 00:19:47.000000000 
+0100
@@ -103,6 +103,8 @@
         self.result = result
         try:
             self._exceptions = []
+            self.case.__testtools_tb_locals__ = getattr(
+                result, 'tb_locals', False)
             self._run_core()
             if self._exceptions:
                 # One or more caught exceptions, now trigger the test's
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/testcase.py 
new/testtools-1.7.1/testtools/testcase.py
--- old/testtools-1.5.0/testtools/testcase.py   2014-11-17 19:40:37.000000000 
+0100
+++ new/testtools-1.7.1/testtools/testcase.py   2015-03-10 00:19:47.000000000 
+0100
@@ -24,8 +24,10 @@
 from extras import (
     safe_hasattr,
     try_import,
+    try_imports,
     )
-import unittest2 as unittest
+# To let setup.py work, make this a conditional import.
+unittest = try_imports(['unittest2', 'unittest'])
 
 from testtools import (
     content,
@@ -585,7 +587,9 @@
                 tb_label = '%s-%d' % (tb_label, tb_id)
             if tb_label not in self.getDetails():
                 break
-        self.addDetail(tb_label, content.TracebackContent(exc_info, self))
+        self.addDetail(tb_label, content.TracebackContent(
+            exc_info, self, capture_locals=getattr(
+                self, '__testtools_tb_locals__', False)))
 
     @staticmethod
     def _report_unexpected_success(self, result, err):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/testresult/real.py 
new/testtools-1.7.1/testtools/testresult/real.py
--- old/testtools-1.5.0/testtools/testresult/real.py    2014-09-03 
06:08:31.000000000 +0200
+++ new/testtools-1.7.1/testtools/testresult/real.py    2015-03-11 
01:03:38.000000000 +0100
@@ -23,6 +23,7 @@
     ]
 
 import datetime
+import math
 from operator import methodcaller
 import sys
 import unittest
@@ -80,12 +81,13 @@
     :ivar skip_reasons: A dict of skip-reasons -> list of tests. See addSkip.
     """
 
-    def __init__(self, failfast=False):
+    def __init__(self, failfast=False, tb_locals=False):
         # startTestRun resets all attributes, and older clients don't know to
         # call startTestRun, so it is called once here.
         # Because subclasses may reasonably not expect this, we call the
         # specific version we want to run.
         self.failfast = failfast
+        self.tb_locals = tb_locals
         TestResult.startTestRun(self)
 
     def addExpectedFailure(self, test, err=None, details=None):
@@ -174,7 +176,8 @@
     def _err_details_to_string(self, test, err=None, details=None):
         """Convert an error in exc_info form or a contents dict to a string."""
         if err is not None:
-            return TracebackContent(err, test).as_text()
+            return TracebackContent(
+                err, test, capture_locals=self.tb_locals).as_text()
         return _details_to_str(details, special='traceback')
 
     def _exc_info_to_unicode(self, err, test):
@@ -201,8 +204,9 @@
         pristine condition ready for use in another test run.  Note that this
         is different from Python 2.7's startTestRun, which does nothing.
         """
-        # failfast is reset by the super __init__, so stash it.
+        # failfast and tb_locals are reset by the super __init__, so save them.
         failfast = self.failfast
+        tb_locals = self.tb_locals
         super(TestResult, self).__init__()
         self.skip_reasons = {}
         self.__now = None
@@ -212,6 +216,8 @@
         self.unexpectedSuccesses = []
         self.failfast = failfast
         # -- End:   As per python 2.7 --
+        # -- Python 3.5
+        self.tb_locals = tb_locals
 
     def stopTestRun(self):
         """Called after a test run completes
@@ -875,16 +881,22 @@
 class TextTestResult(TestResult):
     """A TestResult which outputs activity to a text stream."""
 
-    def __init__(self, stream, failfast=False):
+    def __init__(self, stream, failfast=False, tb_locals=False):
         """Construct a TextTestResult writing to stream."""
-        super(TextTestResult, self).__init__(failfast=failfast)
+        super(TextTestResult, self).__init__(
+            failfast=failfast, tb_locals=tb_locals)
         self.stream = stream
         self.sep1 = '=' * 70 + '\n'
         self.sep2 = '-' * 70 + '\n'
 
-    def _delta_to_float(self, a_timedelta):
-        return (a_timedelta.days * 86400.0 + a_timedelta.seconds +
-            a_timedelta.microseconds / 1000000.0)
+    def _delta_to_float(self, a_timedelta, precision):
+        # This calls ceiling to ensure that the most pessimistic view of time
+        # taken is shown (rather than leaving it to the Python %f operator
+        # to decide whether to round/floor/ceiling. This was added when we
+        # had pyp3 test failures that suggest a floor was happening.
+        shift = 10 ** precision
+        return math.ceil((a_timedelta.days * 86400.0 + a_timedelta.seconds +
+            a_timedelta.microseconds / 1000000.0) * shift) / shift
 
     def _show_list(self, label, error_list):
         for test, output in error_list:
@@ -912,7 +924,7 @@
                     self.sep1, test.id(), self.sep2))
         self.stream.write("\nRan %d test%s in %.3fs\n" %
             (self.testsRun, plural,
-             self._delta_to_float(stop - self.__start)))
+             self._delta_to_float(stop - self.__start, 3)))
         if self.wasSuccessful():
             self.stream.write("OK\n")
         else:
@@ -1340,6 +1352,8 @@
                         self.status(file_name=name, file_bytes=file_bytes,
                             mime_type=mime_type, test_id=test_id, 
timestamp=now)
                     file_bytes = next_bytes
+                if file_bytes is None:
+                    file_bytes = _b("")
                 self.status(file_name=name, file_bytes=file_bytes, eof=True,
                     mime_type=mime_type, test_id=test_id, timestamp=now)
         if reason is not None:
@@ -1642,7 +1656,8 @@
     def _err_to_details(self, test, err, details):
         if details:
             return details
-        return {'traceback': TracebackContent(err, test)}
+        return {'traceback': TracebackContent(
+            err, test, capture_locals=self.tb_locals)}
 
     def addSuccess(self, test, details=None):
         super(TestByTestResult, self).addSuccess(test)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_compat.py 
new/testtools-1.7.1/testtools/tests/test_compat.py
--- old/testtools-1.5.0/testtools/tests/test_compat.py  2013-11-29 
23:52:10.000000000 +0100
+++ new/testtools-1.7.1/testtools/tests/test_compat.py  2015-03-10 
00:19:47.000000000 +0100
@@ -3,7 +3,7 @@
 """Tests for miscellaneous compatibility functions"""
 
 import io
-import linecache
+import linecache2 as linecache
 import os
 import sys
 import tempfile
@@ -13,11 +13,6 @@
 
 from testtools.compat import (
     _b,
-    _detect_encoding,
-    _format_exc_info,
-    _format_exception_only,
-    _format_stack_list,
-    _get_source_encoding,
     _u,
     reraise,
     str_is_unicode,
@@ -34,161 +29,6 @@
     )
 
 
-class TestDetectEncoding(testtools.TestCase):
-    """Test detection of Python source encodings"""
-
-    def _check_encoding(self, expected, lines, possibly_invalid=False):
-        """Check lines are valid Python and encoding is as expected"""
-        if not possibly_invalid:
-            compile(_b("".join(lines)), "<str>", "exec")
-        encoding = _detect_encoding(lines)
-        self.assertEqual(expected, encoding,
-            "Encoding %r expected but got %r from lines %r" %
-                (expected, encoding, lines))
-
-    def test_examples_from_pep(self):
-        """Check the examples given in PEP 263 all work as specified
-
-        See 'Examples' section of <http://www.python.org/dev/peps/pep-0263/>
-        """
-        # With interpreter binary and using Emacs style file encoding comment:
-        self._check_encoding("latin-1", (
-            "#!/usr/bin/python\n",
-            "# -*- coding: latin-1 -*-\n",
-            "import os, sys\n"))
-        self._check_encoding("iso-8859-15", (
-            "#!/usr/bin/python\n",
-            "# -*- coding: iso-8859-15 -*-\n",
-            "import os, sys\n"))
-        self._check_encoding("ascii", (
-            "#!/usr/bin/python\n",
-            "# -*- coding: ascii -*-\n",
-            "import os, sys\n"))
-        # Without interpreter line, using plain text:
-        self._check_encoding("utf-8", (
-            "# This Python file uses the following encoding: utf-8\n",
-            "import os, sys\n"))
-        # Text editors might have different ways of defining the file's
-        # encoding, e.g.
-        self._check_encoding("latin-1", (
-            "#!/usr/local/bin/python\n",
-            "# coding: latin-1\n",
-            "import os, sys\n"))
-        # Without encoding comment, Python's parser will assume ASCII text:
-        self._check_encoding("ascii", (
-            "#!/usr/local/bin/python\n",
-            "import os, sys\n"))
-        # Encoding comments which don't work:
-        #   Missing "coding:" prefix:
-        self._check_encoding("ascii", (
-            "#!/usr/local/bin/python\n",
-            "# latin-1\n",
-            "import os, sys\n"))
-        #   Encoding comment not on line 1 or 2:
-        self._check_encoding("ascii", (
-            "#!/usr/local/bin/python\n",
-            "#\n",
-            "# -*- coding: latin-1 -*-\n",
-            "import os, sys\n"))
-        #   Unsupported encoding:
-        self._check_encoding("ascii", (
-            "#!/usr/local/bin/python\n",
-            "# -*- coding: utf-42 -*-\n",
-            "import os, sys\n"),
-            possibly_invalid=True)
-
-    def test_bom(self):
-        """Test the UTF-8 BOM counts as an encoding declaration"""
-        self._check_encoding("utf-8", (
-            "\xef\xbb\xbfimport sys\n",
-            ))
-        self._check_encoding("utf-8", (
-            "\xef\xbb\xbf# File encoding: utf-8\n",
-            ))
-        self._check_encoding("utf-8", (
-            '\xef\xbb\xbf"""Module docstring\n',
-            '\xef\xbb\xbfThat should just be a ZWNB"""\n'))
-        self._check_encoding("latin-1", (
-            '"""Is this coding: latin-1 or coding: utf-8 instead?\n',
-            '\xef\xbb\xbfThose should be latin-1 bytes"""\n'))
-        self._check_encoding("utf-8", (
-            "\xef\xbb\xbf# Is the coding: utf-8 or coding: euc-jp instead?\n",
-            '"""Module docstring say \xe2\x98\x86"""\n'),
-            possibly_invalid=True)
-
-    def test_multiple_coding_comments(self):
-        """Test only the first of multiple coding declarations counts"""
-        self._check_encoding("iso-8859-1", (
-            "# Is the coding: iso-8859-1\n",
-            "# Or is it coding: iso-8859-2\n"),
-            possibly_invalid=True)
-        self._check_encoding("iso-8859-1", (
-            "#!/usr/bin/python\n",
-            "# Is the coding: iso-8859-1\n",
-            "# Or is it coding: iso-8859-2\n"))
-        self._check_encoding("iso-8859-1", (
-            "# Is the coding: iso-8859-1 or coding: iso-8859-2\n",
-            "# Or coding: iso-8859-3 or coding: iso-8859-4\n"),
-            possibly_invalid=True)
-        self._check_encoding("iso-8859-2", (
-            "# Is the coding iso-8859-1 or coding: iso-8859-2\n",
-            "# Spot the missing colon above\n"))
-
-
-class TestGetSourceEncoding(testtools.TestCase):
-    """Test reading and caching the encodings of source files"""
-
-    def setUp(self):
-        testtools.TestCase.setUp(self)
-        dir = tempfile.mkdtemp()
-        self.addCleanup(os.rmdir, dir)
-        self.filename = os.path.join(dir, self.id().rsplit(".", 1)[1] + ".py")
-        self._written = False
-
-    def put_source(self, text):
-        f = open(self.filename, "w")
-        try:
-            f.write(text)
-        finally:
-            f.close()
-            if not self._written:
-                self._written = True
-                self.addCleanup(os.remove, self.filename)
-                self.addCleanup(linecache.cache.pop, self.filename, None)
-
-    def test_nonexistant_file_as_ascii(self):
-        """When file can't be found, the encoding should default to ascii"""
-        self.assertEquals("ascii", _get_source_encoding(self.filename))
-
-    def test_encoding_is_cached(self):
-        """The encoding should stay the same if the cache isn't invalidated"""
-        self.put_source(
-            "# coding: iso-8859-13\n"
-            "import os\n")
-        self.assertEquals("iso-8859-13", _get_source_encoding(self.filename))
-        self.put_source(
-            "# coding: rot-13\n"
-            "vzcbeg bf\n")
-        self.assertEquals("iso-8859-13", _get_source_encoding(self.filename))
-
-    def test_traceback_rechecks_encoding(self):
-        """A traceback function checks the cache and resets the encoding"""
-        self.put_source(
-            "# coding: iso-8859-8\n"
-            "import os\n")
-        self.assertEquals("iso-8859-8", _get_source_encoding(self.filename))
-        self.put_source(
-            "# coding: utf-8\n"
-            "import os\n")
-        try:
-            exec (compile("raise RuntimeError\n", self.filename, "exec"))
-        except RuntimeError:
-            traceback.extract_tb(sys.exc_info()[2])
-        else:
-            self.fail("RuntimeError not raised")
-        self.assertEquals("utf-8", _get_source_encoding(self.filename))
-
-
 class _FakeOutputStream(object):
     """A simple file-like object for testing"""
 
@@ -453,151 +293,6 @@
         self.assertRaises(CustomException, reraise, *_exc_info)
 
 
-class Python2CompatibilityTests(testtools.TestCase):
-
-    def setUp(self):
-        super(Python2CompatibilityTests, self).setUp()
-        if sys.version[0] >= '3':
-            self.skip("These tests are only applicable to python 2.")
-
-
-class TestExceptionFormatting(Python2CompatibilityTests):
-    """Test the _format_exception_only function."""
-
-    def _assert_exception_format(self, eclass, evalue, expected):
-        actual = _format_exception_only(eclass, evalue)
-        self.assertThat(actual, Equals(expected))
-        self.assertThat(''.join(actual), IsInstance(unicode))
-
-    def test_supports_string_exception(self):
-        self._assert_exception_format(
-            "String_Exception",
-            None,
-            [_u("String_Exception\n")]
-        )
-
-    def test_supports_regular_exception(self):
-        self._assert_exception_format(
-            RuntimeError,
-            RuntimeError("Something went wrong"),
-            [_u("RuntimeError: Something went wrong\n")]
-        )
-
-    def test_supports_unprintable_exceptions(self):
-        """Verify support for exception classes that raise an exception when
-        __unicode__ or __str__ is called.
-        """
-        class UnprintableException(Exception):
-
-            def __str__(self):
-                raise Exception()
-
-            def __unicode__(self):
-                raise Exception()
-
-        self._assert_exception_format(
-            UnprintableException,
-            UnprintableException("Foo"),
-            [_u("UnprintableException: <unprintable UnprintableException 
object>\n")]
-        )
-
-    def test_supports_exceptions_with_no_string_value(self):
-        class NoStringException(Exception):
-
-            def __str__(self):
-                return ""
-
-            def __unicode__(self):
-                return _u("")
-
-        self._assert_exception_format(
-            NoStringException,
-            NoStringException("Foo"),
-            [_u("NoStringException\n")]
-        )
-
-    def test_supports_strange_syntax_error(self):
-        """Test support for syntax errors with unusual number of arguments"""
-        self._assert_exception_format(
-            SyntaxError,
-            SyntaxError("Message"),
-            [_u("SyntaxError: Message\n")]
-        )
-
-    def test_supports_syntax_error(self):
-        self._assert_exception_format(
-            SyntaxError,
-            SyntaxError(
-                "Some Syntax Message",
-                (
-                    "/path/to/file",
-                    12,
-                    2,
-                    "This is the line of code",
-                )
-            ),
-            [
-                _u('  File "/path/to/file", line 12\n'),
-                _u('    This is the line of code\n'),
-                _u('     ^\n'),
-                _u('SyntaxError: Some Syntax Message\n'),
-            ]
-        )
-
-
-class StackListFormattingTests(Python2CompatibilityTests):
-    """Test the _format_stack_list function."""
-
-    def _assert_stack_format(self, stack_lines, expected_output):
-        actual = _format_stack_list(stack_lines)
-        self.assertThat(actual, Equals([expected_output]))
-
-    def test_single_complete_stack_line(self):
-        stack_lines = [(
-            '/path/to/filename',
-            12,
-            'func_name',
-            'some_code()',
-        )]
-        expected = \
-            _u('  File "/path/to/filename", line 12, in func_name\n' \
-               '    some_code()\n')
-
-        self._assert_stack_format(stack_lines, expected)
-
-    def test_single_stack_line_no_code(self):
-        stack_lines = [(
-            '/path/to/filename',
-            12,
-            'func_name',
-            None
-        )]
-        expected = _u('  File "/path/to/filename", line 12, in func_name\n')
-        self._assert_stack_format(stack_lines, expected)
-
-
-class FormatExceptionInfoTests(Python2CompatibilityTests):
-
-    def test_individual_functions_called(self):
-        self.patch(
-            testtools.compat,
-            '_format_stack_list',
-            lambda stack_list: [_u("format stack list called\n")]
-        )
-        self.patch(
-            testtools.compat,
-            '_format_exception_only',
-            lambda etype, evalue: [_u("format exception only called\n")]
-        )
-        result = _format_exc_info(None, None, None)
-        expected = [
-            _u("Traceback (most recent call last):\n"),
-            _u("format stack list called\n"),
-            _u("format exception only called\n"),
-        ]
-        self.assertThat(expected, Equals(result))
-
-
 def test_suite():
     from unittest import TestLoader
     return TestLoader().loadTestsFromName(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_content.py 
new/testtools-1.7.1/testtools/tests/test_content.py
--- old/testtools-1.5.0/testtools/tests/test_content.py 2014-10-20 
04:16:48.000000000 +0200
+++ new/testtools-1.7.1/testtools/tests/test_content.py 2015-03-10 
00:19:47.000000000 +0100
@@ -278,24 +278,20 @@
         content = StacktraceContent()
         content_type = ContentType("text", "x-traceback",
             {"language": "python", "charset": "utf8"})
-
         self.assertEqual(content_type, content.content_type)
 
     def test_prefix_is_used(self):
         prefix = self.getUniqueString()
         actual = StacktraceContent(prefix_content=prefix).as_text()
-
         self.assertTrue(actual.startswith(prefix))
 
     def test_postfix_is_used(self):
         postfix = self.getUniqueString()
         actual = StacktraceContent(postfix_content=postfix).as_text()
-
         self.assertTrue(actual.endswith(postfix))
 
     def test_top_frame_is_skipped_when_no_stack_is_specified(self):
         actual = StacktraceContent().as_text()
-
         self.assertTrue('testtools/content.py' not in actual)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_run.py 
new/testtools-1.7.1/testtools/tests/test_run.py
--- old/testtools-1.5.0/testtools/tests/test_run.py     2014-11-18 
22:59:06.000000000 +0100
+++ new/testtools-1.7.1/testtools/tests/test_run.py     2015-03-10 
00:19:47.000000000 +0100
@@ -307,6 +307,18 @@
         self.assertThat(
             stdout.getDetails()['stdout'].as_text(), Contains('Ran 1 test'))
 
+    def test_run_locals(self):
+        stdout = self.useFixture(fixtures.StringStream('stdout'))
+
+        class Failing(TestCase):
+            def test_a(self):
+                a = 1
+                self.fail('a')
+        runner = run.TestToolsTestRunner(tb_locals=True, stdout=stdout.stream)
+        runner.run(Failing('test_a'))
+        self.assertThat(
+            stdout.getDetails()['stdout'].as_text(), Contains('a = 1'))
+
     def test_stdout_honoured(self):
         self.useFixture(SampleTestFixture())
         tests = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_testresult.py 
new/testtools-1.7.1/testtools/tests/test_testresult.py
--- old/testtools-1.5.0/testtools/tests/test_testresult.py      2014-10-20 
04:16:48.000000000 +0200
+++ new/testtools-1.7.1/testtools/tests/test_testresult.py      2015-03-11 
00:48:49.000000000 +0100
@@ -95,6 +95,7 @@
 def make_erroring_test():
     class Test(TestCase):
         def error(self):
+            a = 1
             1/0
     return Test("error")
 
@@ -847,6 +848,53 @@
         result.stopTestRun()
         self.assertEqual(False, result.wasSuccessful())
 
+    def test_empty_detail_status_correct(self):
+        log = LoggingStreamResult()
+        result = ExtendedToStreamDecorator(log)
+        result.startTestRun()
+        now = datetime.datetime.now(utc)
+        result.time(now)
+        result.startTest(self)
+        result.addError(self, details={'foo': text_content(_u(""))})
+        result.stopTest(self)
+        result.stopTestRun()
+        self.assertEqual([
+            ('startTestRun',),
+            ('status',
+             
'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct',
+             'inprogress',
+             None,
+             True,
+             None,
+             None,
+             False,
+             None,
+             None,
+             now),
+            ('status',
+             
'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct',
+              None,
+              None,
+              True,
+              'foo',
+              _b(''),
+              True,
+              'text/plain; charset="utf8"',
+              None,
+              now),
+            ('status',
+             
'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct',
+             'fail',
+              set(),
+              True,
+              None,
+              None,
+              False,
+              None,
+              None,
+              now),
+             ('stopTestRun',)], log._events)
+
 
 class TestStreamFailFast(TestCase):
 
@@ -1163,6 +1211,32 @@
         self.assertEqual(
             TracebackContent(exc_info, test).as_text(), text_traceback)
 
+    def test_traceback_with_locals(self):
+        result = self.makeResult()
+        result.tb_locals = True
+        test = make_erroring_test()
+        test.run(result)
+        self.assertThat(
+            result.errors[0][1],
+            DocTestMatches(
+                'Traceback (most recent call last):\n'
+                '  File "...testtools...runtest.py", line ..., in _run_user\n'
+                '    return fn(*args, **kwargs)\n'
+                '    args = ...\n'
+                '    fn = ...\n'
+                '    kwargs = ...\n'
+                '    self = ...\n'
+                '  File "...testtools...testcase.py", line ..., in 
_run_test_method\n'
+                '    return self._get_test_method()()\n'
+                '    result = ...\n'
+                '    self = ...\n'
+                '  File "...testtools...tests...test_testresult.py", line ..., 
in error\n'
+                '    1/0\n'
+                '    a = 1\n'
+                '    self = ...\n'
+                'ZeroDivisionError: ...\n',
+                doctest.ELLIPSIS | doctest.REPORT_UDIFF))
+
 
 class TestMultiTestResult(TestCase):
     """Tests for 'MultiTestResult'."""
@@ -2445,8 +2519,7 @@
         if str_is_unicode:
             output_text = example_text
         else:
-            output_text = example_text.encode("shift_jis").decode(
-                _get_exception_encoding(), "replace")
+            output_text = "b%r" % example_text.encode("shift_jis")
         self.assertIn(self._as_output("AssertionError: %s" % output_text),
             textoutput)
 
@@ -2515,19 +2588,6 @@
         textoutput = self._test_external_case("raise SyntaxError(3, 2, 1)")
         self.assertIn(self._as_output("\nSyntaxError: "), textoutput)
 
-    def test_syntax_error_import_binary(self):
-        """Importing a binary file shouldn't break SyntaxError formatting"""
-        self._setup_external_case("import bad")
-        f = open(os.path.join(self.dir, "bad.py"), "wb")
-        try:
-            f.write(_b("x\x9c\xcb*\xcd\xcb\x06\x00\x04R\x01\xb9"))
-        finally:
-            f.close()
-        textoutput = self._run_external_case()
-        matches_error = MatchesAny(
-            Contains('\nTypeError: '), Contains('\nSyntaxError: '))
-        self.assertThat(textoutput, matches_error)
-
     def test_syntax_error_line_iso_8859_1(self):
         """Syntax error on a latin-1 line shows the line decoded"""
         text, raw = self._get_sample_text("iso-8859-1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_testsuite.py 
new/testtools-1.7.1/testtools/tests/test_testsuite.py
--- old/testtools-1.5.0/testtools/tests/test_testsuite.py       2014-11-17 
19:40:37.000000000 +0100
+++ new/testtools-1.7.1/testtools/tests/test_testsuite.py       2015-03-10 
00:19:47.000000000 +0100
@@ -179,17 +179,32 @@
         suite.run(result)
         events = result._events
         # Check the traceback loosely.
-        self.assertThat(events[1][6].decode('utf8'), DocTestMatches("""\
-Traceback (most recent call last):
+        self.assertEqual(events[1][6].decode('utf8'),
+            "Traceback (most recent call last):\n")
+        self.assertThat(events[2][6].decode('utf8'), DocTestMatches("""\
   File "...testtools/testsuite.py", line ..., in _run_test
     test.run(process_result)
+""", doctest.ELLIPSIS))
+        self.assertThat(events[3][6].decode('utf8'), DocTestMatches("""\
 TypeError: run() takes ...1 ...argument...2...given...
 """, doctest.ELLIPSIS))
         events = [event[0:10] + (None,) for event in events]
         events[1] = events[1][:6] + (None,) + events[1][7:]
+        events[2] = events[2][:6] + (None,) + events[2][7:]
+        events[3] = events[3][:6] + (None,) + events[3][7:]
         self.assertEqual([
             ('status', "broken-runner-'0'", 'inprogress', None, True, None, 
None, False, None, _u('0'), None),
             ('status', "broken-runner-'0'", None, None, True, 'traceback', 
None,
+             False,
+             'text/x-traceback; charset="utf8"; language="python"',
+             '0',
+             None),
+            ('status', "broken-runner-'0'", None, None, True, 'traceback', 
None,
+             False,
+             'text/x-traceback; charset="utf8"; language="python"',
+             '0',
+             None),
+            ('status', "broken-runner-'0'", None, None, True, 'traceback', 
None,
              True,
              'text/x-traceback; charset="utf8"; language="python"',
              '0',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools/testsuite.py 
new/testtools-1.7.1/testtools/testsuite.py
--- old/testtools-1.5.0/testtools/testsuite.py  2014-11-15 21:44:58.000000000 
+0100
+++ new/testtools-1.7.1/testtools/testsuite.py  2015-03-10 00:19:47.000000000 
+0100
@@ -14,10 +14,10 @@
 import sys
 import threading
 import unittest
-import unittest2
 
 from extras import safe_hasattr, try_imports
-
+# This is just to let setup.py work, as testtools is imported in setup.py.
+unittest2 = try_imports(['unittest2', 'unittest'])
 Queue = try_imports(['Queue.Queue', 'queue.Queue'])
 
 import testtools
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools.egg-info/PKG-INFO 
new/testtools-1.7.1/testtools.egg-info/PKG-INFO
--- old/testtools-1.5.0/testtools.egg-info/PKG-INFO     2014-11-21 
00:20:56.000000000 +0100
+++ new/testtools-1.7.1/testtools.egg-info/PKG-INFO     2015-03-11 
02:18:21.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: testtools
-Version: 1.5.0
+Version: 1.7.1
 Summary: Extensions to the Python standard library unit testing framework
 Home-page: https://github.com/testing-cabal/testtools
 Author: Jonathan M. Lange
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.5.0/testtools.egg-info/requires.txt 
new/testtools-1.7.1/testtools.egg-info/requires.txt
--- old/testtools-1.5.0/testtools.egg-info/requires.txt 2014-11-21 
00:20:56.000000000 +0100
+++ new/testtools-1.7.1/testtools.egg-info/requires.txt 2015-03-11 
02:18:21.000000000 +0100
@@ -1,3 +1,4 @@
 extras
 python-mimeparse
-unittest2>=0.8.0
\ No newline at end of file
+unittest2>=1.0.0
+traceback2
\ No newline at end of file

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to