[Python-checkins] r72477 - in python/branches/py3k: Doc/c-api/init.rst Doc/howto/functional.rst Doc/howto/regex.rst Doc/library/2to3.rst Doc/library/collections.rst Doc/library/decimal.rst Doc/library/json.rst Doc/library/shelve.rst Doc/library/subprocess.rst Doc/library/traceback.rst Doc/library/unittest.rst Doc/whatsnew/2.7.rst Lib/collections.py Lib/logging/__init__.py Lib/test/regrtest.py Lib/test/test_aifc.py Lib/test/test_descr.py Lib/test/test_logging.py Lib/test/test_shutil.py Lib/test/test_unittest.py Lib/unittest.py Modules/_ctypes/libffi.diff

benjamin.peterson python-checkins at python.org
Fri May 8 22:42:27 CEST 2009


Author: benjamin.peterson
Date: Fri May  8 22:42:26 2009
New Revision: 72477

Log:
Merged revisions 70768,71657,71721,71729,71794,71976,72036-72037,72079,72085,72131-72134,72191,72197-72198,72219,72221,72225,72303,72434,72467,72476 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r70768 | andrew.kuchling | 2009-03-30 17:29:15 -0500 (Mon, 30 Mar 2009) | 1 line
  
  Typo fixes
........
  r71657 | vinay.sajip | 2009-04-16 14:07:37 -0500 (Thu, 16 Apr 2009) | 1 line
  
  Issue #5768: Change to Unicode output logic and test case for same.
........
  r71721 | benjamin.peterson | 2009-04-18 14:26:19 -0500 (Sat, 18 Apr 2009) | 1 line
  
  fix a few nits in unittest.py #5771
........
  r71729 | benjamin.peterson | 2009-04-18 16:03:10 -0500 (Sat, 18 Apr 2009) | 1 line
  
  move test to a more appropiate one
........
  r71794 | vinay.sajip | 2009-04-22 07:10:47 -0500 (Wed, 22 Apr 2009) | 2 lines
  
  Issue #5170: Fixed regression caused when fixing #5768.
........
  r71976 | mark.dickinson | 2009-04-26 14:54:55 -0500 (Sun, 26 Apr 2009) | 2 lines
  
  Fix typo in function name
........
  r72036 | georg.brandl | 2009-04-27 12:04:23 -0500 (Mon, 27 Apr 2009) | 1 line
  
  #5848: small unittest doc patch.
........
  r72037 | georg.brandl | 2009-04-27 12:09:53 -0500 (Mon, 27 Apr 2009) | 1 line
  
  #5840: dont claim we dont support TLS.
........
  r72079 | r.david.murray | 2009-04-28 14:02:55 -0500 (Tue, 28 Apr 2009) | 2 lines
  
  Remove spurious 'u'.
........
  r72085 | georg.brandl | 2009-04-28 16:48:35 -0500 (Tue, 28 Apr 2009) | 1 line
  
  Make the doctests in the docs pass, except for those in the turtle module.
........
  r72131 | benjamin.peterson | 2009-04-29 17:43:35 -0500 (Wed, 29 Apr 2009) | 1 line
  
  fix test_shutil on ZFS #5676
........
  r72132 | georg.brandl | 2009-04-29 17:44:07 -0500 (Wed, 29 Apr 2009) | 1 line
  
  #5878: fix repr of re object.
........
  r72133 | benjamin.peterson | 2009-04-29 17:44:15 -0500 (Wed, 29 Apr 2009) | 1 line
  
  make sure mode is removable while cleaning up test droppings
........
  r72134 | benjamin.peterson | 2009-04-29 19:06:33 -0500 (Wed, 29 Apr 2009) | 1 line
  
  make sure to close file
........
  r72191 | michael.foord | 2009-05-02 06:43:06 -0500 (Sat, 02 May 2009) | 9 lines
  
  Adds an exit parameter to unittest.main(). If False main no longer
  calls sys.exit.
  
  Closes issue 3379.
  
  Michael Foord
   
........
  r72197 | benjamin.peterson | 2009-05-02 11:24:37 -0500 (Sat, 02 May 2009) | 1 line
  
  don't let sys.argv be used in the tests
........
  r72198 | andrew.kuchling | 2009-05-02 12:12:15 -0500 (Sat, 02 May 2009) | 1 line
  
  Add items
........
  r72219 | michael.foord | 2009-05-02 15:15:05 -0500 (Sat, 02 May 2009) | 8 lines
  
  Add addCleanup and doCleanups to unittest.TestCase.
  
  Closes issue 5679.
  
  Michael Foord
........
  r72221 | benjamin.peterson | 2009-05-02 15:26:53 -0500 (Sat, 02 May 2009) | 1 line
  
  add myself
........
  r72225 | michael.foord | 2009-05-02 17:43:34 -0500 (Sat, 02 May 2009) | 1 line
........
  r72303 | benjamin.peterson | 2009-05-04 19:55:24 -0500 (Mon, 04 May 2009) | 1 line
  
  using sys._getframe(x), where x > 0 doesnt' work on IronPython
........
  r72434 | r.david.murray | 2009-05-07 13:09:58 -0500 (Thu, 07 May 2009) | 2 lines
  
  Pre-opened test file needs to be opened in binary mode.
........
  r72467 | georg.brandl | 2009-05-08 07:17:34 -0500 (Fri, 08 May 2009) | 1 line
  
  Fix name.
........
  r72476 | thomas.heller | 2009-05-08 15:09:40 -0500 (Fri, 08 May 2009) | 4 lines
  
  Add a file that contains diffs between offical libffi files and the
  files in this repository.  Should make it easier to merge new libffi
  versions.
........


Added:
   python/branches/py3k/Modules/_ctypes/libffi.diff
      - copied unchanged from r72476, /python/trunk/Modules/_ctypes/libffi.diff
Modified:
   python/branches/py3k/   (props changed)
   python/branches/py3k/Doc/c-api/init.rst
   python/branches/py3k/Doc/howto/functional.rst
   python/branches/py3k/Doc/howto/regex.rst
   python/branches/py3k/Doc/library/2to3.rst
   python/branches/py3k/Doc/library/collections.rst
   python/branches/py3k/Doc/library/decimal.rst
   python/branches/py3k/Doc/library/json.rst
   python/branches/py3k/Doc/library/shelve.rst
   python/branches/py3k/Doc/library/subprocess.rst
   python/branches/py3k/Doc/library/traceback.rst
   python/branches/py3k/Doc/library/unittest.rst
   python/branches/py3k/Doc/whatsnew/2.7.rst
   python/branches/py3k/Lib/collections.py
   python/branches/py3k/Lib/logging/__init__.py
   python/branches/py3k/Lib/test/regrtest.py
   python/branches/py3k/Lib/test/test_aifc.py
   python/branches/py3k/Lib/test/test_descr.py
   python/branches/py3k/Lib/test/test_logging.py
   python/branches/py3k/Lib/test/test_shutil.py
   python/branches/py3k/Lib/test/test_unittest.py
   python/branches/py3k/Lib/unittest.py

Modified: python/branches/py3k/Doc/c-api/init.rst
==============================================================================
--- python/branches/py3k/Doc/c-api/init.rst	(original)
+++ python/branches/py3k/Doc/c-api/init.rst	Fri May  8 22:42:26 2009
@@ -416,10 +416,9 @@
 The Python interpreter needs to keep some bookkeeping information separate per
 thread --- for this it uses a data structure called :ctype:`PyThreadState`.
 There's one global variable, however: the pointer to the current
-:ctype:`PyThreadState` structure.  While most thread packages have a way to
-store "per-thread global data," Python's internal platform independent thread
-abstraction doesn't support this yet.  Therefore, the current thread state must
-be manipulated explicitly.
+:ctype:`PyThreadState` structure.  Before the addition of :dfn:`thread-local
+storage` (:dfn:`TLS`) the current thread state had to be manipulated
+explicitly.
 
 This is easy enough in most cases.  Most code manipulating the global
 interpreter lock has the following simple structure::

Modified: python/branches/py3k/Doc/howto/functional.rst
==============================================================================
--- python/branches/py3k/Doc/howto/functional.rst	(original)
+++ python/branches/py3k/Doc/howto/functional.rst	Fri May  8 22:42:26 2009
@@ -473,7 +473,7 @@
 
     >>> gen = generate_ints(3)
     >>> gen
-    <generator object at ...>
+    <generator object generate_ints at ...>
     >>> next(gen)
     0
     >>> next(gen)

Modified: python/branches/py3k/Doc/howto/regex.rst
==============================================================================
--- python/branches/py3k/Doc/howto/regex.rst	(original)
+++ python/branches/py3k/Doc/howto/regex.rst	Fri May  8 22:42:26 2009
@@ -264,7 +264,7 @@
    >>> import re
    >>> p = re.compile('ab*')
    >>> p
-   <re.RegexObject instance at 80b4150>
+   <_sre.SRE_Pattern object at 80b4150>
 
 :func:`re.compile` also accepts an optional *flags* argument, used to enable
 various special features and syntax variations.  We'll go over the available

Modified: python/branches/py3k/Doc/library/2to3.rst
==============================================================================
--- python/branches/py3k/Doc/library/2to3.rst	(original)
+++ python/branches/py3k/Doc/library/2to3.rst	Fri May  8 22:42:26 2009
@@ -352,6 +352,7 @@
    :synopsis: the 2to3 library
 .. moduleauthor:: Guido van Rossum
 .. moduleauthor:: Collin Winter
+.. moduleauthor:: Benjamin Peterson <benjamin at python.org>
 
 
 .. note::

Modified: python/branches/py3k/Doc/library/collections.rst
==============================================================================
--- python/branches/py3k/Doc/library/collections.rst	(original)
+++ python/branches/py3k/Doc/library/collections.rst	Fri May  8 22:42:26 2009
@@ -169,7 +169,7 @@
    class is similar to bags or multisets in other languages.
 
    Elements are counted from an *iterable* or initialized from another
-   *mapping* (or counter)::
+   *mapping* (or counter):
 
         >>> c = Counter()                           # a new, empty counter
         >>> c = Counter('gallahad')                 # a new counter from an iterable
@@ -177,7 +177,7 @@
         >>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args
 
    Counter objects have a dictionary interface except that they return a zero
-   count for missing items instead of raising a :exc:`KeyError`::
+   count for missing items instead of raising a :exc:`KeyError`:
 
         >>> c = Counter(['eggs', 'ham'])
         >>> c['bacon']                              # count of a missing element is zero
@@ -210,7 +210,7 @@
       Return a list of the *n* most common elements and their counts from the
       most common to the least.  If *n* is not specified, :func:`most_common`
       returns *all* elements in the counter.  Elements with equal counts are
-      ordered arbitrarily::
+      ordered arbitrarily:
 
             >>> Counter('abracadabra').most_common(3)
             [('a', 5), ('r', 2), ('b', 2)]

Modified: python/branches/py3k/Doc/library/decimal.rst
==============================================================================
--- python/branches/py3k/Doc/library/decimal.rst	(original)
+++ python/branches/py3k/Doc/library/decimal.rst	Fri May  8 22:42:26 2009
@@ -1746,7 +1746,7 @@
    >>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
    Traceback (most recent call last):
       ...
-   Inexact
+   Inexact: None
 
 Q. Once I have valid two place inputs, how do I maintain that invariant
 throughout an application?

Modified: python/branches/py3k/Doc/library/json.rst
==============================================================================
--- python/branches/py3k/Doc/library/json.rst	(original)
+++ python/branches/py3k/Doc/library/json.rst	Fri May  8 22:42:26 2009
@@ -165,12 +165,12 @@
    document) to a Python object.
 
    *object_hook* is an optional function that will be called with the result of
-   any object literal decode (a :class:`dict`).  The return value of
+   any object literal decoded (a :class:`dict`).  The return value of
    *object_hook* will be used instead of the :class:`dict`.  This feature can be used
    to implement custom decoders (e.g. JSON-RPC class hinting).
 
    *object_pairs_hook* is an optional function that will be called with the
-   result of any object literal decode with an ordered list of pairs.  The
+   result of any object literal decoded with an ordered list of pairs.  The
    return value of *object_pairs_hook* will be used instead of the
    :class:`dict`.  This feature can be used to implement custom decoders that
    rely on the order that the key and value pairs are decoded (for example,

Modified: python/branches/py3k/Doc/library/shelve.rst
==============================================================================
--- python/branches/py3k/Doc/library/shelve.rst	(original)
+++ python/branches/py3k/Doc/library/shelve.rst	Fri May  8 22:42:26 2009
@@ -1,4 +1,3 @@
-
 :mod:`shelve` --- Python object persistence
 ===========================================
 
@@ -35,7 +34,7 @@
    accessed entries are written back (there is no way to determine which accessed
    entries are mutable, nor which ones were actually mutated).
 
-Shelve objects support all methods supported by dictionaries.  This eases the
+Shelf objects support all methods supported by dictionaries.  This eases the
 transition from dictionary based scripts to those requiring persistent storage.
 
 One additional method is supported:

Modified: python/branches/py3k/Doc/library/subprocess.rst
==============================================================================
--- python/branches/py3k/Doc/library/subprocess.rst	(original)
+++ python/branches/py3k/Doc/library/subprocess.rst	Fri May  8 22:42:26 2009
@@ -174,13 +174,13 @@
    :attr:`returncode`
    attribute and output in the :attr:`output` attribute.
 
-   The arguments are the same as for the :class:`Popen` constructor.  Example:
+   The arguments are the same as for the :class:`Popen` constructor.  Example::
 
       >>> subprocess.check_output(["ls", "-l", "/dev/null"])
       'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
 
    The stdout argument is not allowed as it is used internally.
-   To capture standard error in the result, use stderr=subprocess.STDOUT.
+   To capture standard error in the result, use ``stderr=subprocess.STDOUT``::
 
       >>> subprocess.check_output(
               ["/bin/sh", "-c", "ls non_existent_file ; exit 0"],

Modified: python/branches/py3k/Doc/library/traceback.rst
==============================================================================
--- python/branches/py3k/Doc/library/traceback.rst	(original)
+++ python/branches/py3k/Doc/library/traceback.rst	Fri May  8 22:42:26 2009
@@ -228,7 +228,7 @@
    *** extract_tb:
    [('<doctest...>', 10, '<module>', 'lumberjack()'),
     ('<doctest...>', 4, 'lumberjack', 'bright_side_of_death()'),
-    (u'<doctest...>', 7, 'bright_side_of_death', 'return tuple()[0]')]
+    ('<doctest...>', 7, 'bright_side_of_death', 'return tuple()[0]')]
    *** format_tb:
    ['  File "<doctest...>", line 10, in <module>\n    lumberjack()\n',
     '  File "<doctest...>", line 4, in lumberjack\n    bright_side_of_death()\n',

Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst	(original)
+++ python/branches/py3k/Doc/library/unittest.rst	Fri May  8 22:42:26 2009
@@ -954,7 +954,6 @@
       along with the method name.
 
       .. versionchanged:: 3.1
-
          In earlier versions this only returned the first line of the test
          method's docstring, if available or the :const:`None`.  That led to
          undesirable behavior of not printing the test name when someone was
@@ -978,6 +977,36 @@
       .. versionadded:: 3.1
 
 
+   .. method:: addCleanup(function[, *args[, **kwargs]])
+
+      Add a function to be called after :meth:`tearDown` to cleanup resources
+      used during the test. Functions will be called in reverse order to the
+      order they are added (LIFO). They are called with any arguments and
+      keyword arguments passed into :meth:`addCleanup` when they are
+      added.
+
+      If :meth:`setUp` fails, meaning that :meth:`tearDown` is not called,
+      then any cleanup functions added will still be called.
+
+      .. versionadded:: 2.7
+
+
+   .. method:: doCleanups()
+
+      This method is called uncoditionally after :meth:`tearDown`, or
+      after :meth:`setUp` if :meth:`setUp` raises an exception.
+
+      It is responsible for calling all the cleanup functions added by
+      :meth:`addCleanup`. If you need cleanup functions to be called
+      *prior* to :meth:`tearDown` then you can call :meth:`doCleanups`
+      yourself.
+
+      :meth:`doCleanups` pops methods off the stack of cleanup
+      functions one at a time, so it can be called at any time.
+
+      .. versionadded:: 2.7
+
+
 .. class:: FunctionTestCase(testFunc[, setUp[, tearDown[, description]]])
 
    This class implements the portion of the :class:`TestCase` interface which
@@ -1046,6 +1075,20 @@
       Return the number of tests represented by this test object, including all
       individual tests and sub-suites.
 
+
+   .. method:: __iter__()
+
+      Tests grouped by a :class:`TestSuite` are always accessed by iteration.
+      Subclasses can lazily provide tests by overriding :meth:`__iter__`. Note
+      that this method maybe called several times on a single suite
+      (for example when counting tests or comparing for equality)
+      so the tests returned must be the same for repeated iterations.
+
+      .. versionchanged:: 2.7
+         In earlier versions the :class:`TestSuite` accessed tests directly rather
+         than through iteration, so overriding :meth:`__iter__` wasn't sufficient
+         for providing tests.
+
    In the typical usage of a :class:`TestSuite` object, the :meth:`run` method
    is invoked by a :class:`TestRunner` rather than by the end-user test harness.
 
@@ -1190,7 +1233,6 @@
       holding formatted tracebacks. Each tuple represents a test which raised an
       unexpected exception.
 
-
    .. attribute:: failures
 
       A list containing 2-tuples of :class:`TestCase` instances and strings
@@ -1266,6 +1308,20 @@
       The default implementation does nothing.
 
 
+   .. method:: startTestRun(test)
+
+      Called once before any tests are executed.
+
+      .. versionadded:: 2.7
+
+
+   .. method:: stopTestRun(test)
+
+      Called once before any tests are executed.
+
+      .. versionadded:: 2.7
+
+
    .. method:: addError(test, err)
 
       Called when the test case *test* raises an unexpected exception *err* is a
@@ -1335,8 +1391,14 @@
    has a few configurable parameters, but is essentially very simple.  Graphical
    applications which run test suites should provide alternate implementations.
 
+   .. method:: _makeResult()
+
+      This method returns the instance of ``TestResult`` used by :meth:`run`.
+      It is not intended to be called directly, but can be overridden in
+      subclasses to provide a custom ``TestResult``.
 
-.. function:: main([module[, defaultTest[, argv[, testRunner[, testLoader]]]]])
+
+.. function:: main([module[, defaultTest[, argv[, testRunner[, testLoader[, exit]]]]]])
 
    A command-line program that runs a set of tests; this is primarily for making
    test modules conveniently executable.  The simplest use for this function is to
@@ -1346,4 +1408,18 @@
           unittest.main()
 
    The *testRunner* argument can either be a test runner class or an already
-   created instance of it.
+   created instance of it. By default ``main`` calls :func:`sys.exit` with
+   an exit code indicating success or failure of the tests run.
+
+   ``main`` supports being used from the interactive interpreter by passing in the
+   argument ``exit=False``. This displays the result on standard output without
+   calling :func:`sys.exit`::
+
+      >>> from unittest import main
+      >>> main(module='test_module', exit=False)
+
+   Calling ``main`` actually returns an instance of the ``TestProgram`` class.
+   This stores the result of the tests run as the ``result`` attribute.
+
+   .. versionchanged:: 2.7
+      The ``exit`` parameter was added.

Modified: python/branches/py3k/Doc/whatsnew/2.7.rst
==============================================================================
--- python/branches/py3k/Doc/whatsnew/2.7.rst	(original)
+++ python/branches/py3k/Doc/whatsnew/2.7.rst	Fri May  8 22:42:26 2009
@@ -121,6 +121,31 @@
 
   (Contributed by Fredrik Johansson and Victor Stinner; :issue:`3439`.)
 
+* Conversions from long integers and regular integers to floating
+  point now round differently, returning the floating-point number
+  closest to the number.  This doesn't matter for small integers that
+  can be converted exactly, but for large numbers that will
+  unavoidably lose precision, Python 2.7 will now approximate more
+  closely.  For example, Python 2.6 computed the following::
+
+    >>> n = 295147905179352891391
+    >>> float(n)
+    2.9514790517935283e+20
+    >>> n - long(float(n))
+    65535L
+
+  Python 2.7's floating-point result is larger, but much closer to the
+  true value::
+
+    >>> n = 295147905179352891391
+    >>> float(n)
+    2.9514790517935289e+20
+    >>> n-long(float(n)
+    ... )
+    -1L
+
+  (Implemented by Mark Dickinson; :issue:`3166`.)
+
 * The :class:`bytearray` type's :meth:`translate` method will
   now accept ``None`` as its first argument.  (Fixed by Georg Brandl;
   :issue:`4759`.)
@@ -220,21 +245,24 @@
 * New class: the :class:`Counter` class in the :mod:`collections` module is
   useful for tallying data.  :class:`Counter` instances behave mostly
   like dictionaries but return zero for missing keys instead of
-  raising a :exc:`KeyError`::
+  raising a :exc:`KeyError`:
 
-    >>> from collections import Counter
-    >>> c=Counter()
-    >>> for letter in 'here is a sample of english text':
-    ...   c[letter] += 1
-    ...
-    >>> c
-    Counter({' ': 6, 'e': 5, 's': 3, 'a': 2, 'i': 2, 'h': 2,
-    'l': 2, 't': 2, 'g': 1, 'f': 1, 'm': 1, 'o': 1, 'n': 1,
-    'p': 1, 'r': 1, 'x': 1})
-    >>> c['e']
-    5
-    >>> c['z']
-    0
+  .. doctest::
+     :options: +NORMALIZE_WHITESPACE
+
+     >>> from collections import Counter
+     >>> c = Counter()
+     >>> for letter in 'here is a sample of english text':
+     ...   c[letter] += 1
+     ...
+     >>> c
+     Counter({' ': 6, 'e': 5, 's': 3, 'a': 2, 'i': 2, 'h': 2,
+     'l': 2, 't': 2, 'g': 1, 'f': 1, 'm': 1, 'o': 1, 'n': 1,
+     'p': 1, 'r': 1, 'x': 1})
+     >>> c['e']
+     5
+     >>> c['z']
+     0
 
   There are two additional :class:`Counter` methods: :meth:`most_common`
   returns the N most common elements and their counts, and :meth:`elements`
@@ -247,7 +275,7 @@
        'a', 'a', ' ', ' ', ' ', ' ', ' ', ' ',
        'e', 'e', 'e', 'e', 'e', 'g', 'f', 'i', 'i',
        'h', 'h', 'm', 'l', 'l', 'o', 'n', 'p', 's',
-       's', 's', 'r', 't', 't', 'x']
+       's', 's', 'r', 't', 't', 'x'
 
   Contributed by Raymond Hettinger; :issue:`1696199`.
 
@@ -257,7 +285,8 @@
   renamed to legal names that are derived from the field's
   position within the list of fields:
 
-     >>> T=namedtuple('T', ['field1', '$illegal', 'for', 'field2'], rename=True)
+     >>> from collections import namedtuple
+     >>> T = namedtuple('T', ['field1', '$illegal', 'for', 'field2'], rename=True)
      >>> T._fields
      ('field1', '_1', '_2', 'field2')
 
@@ -294,6 +323,10 @@
   ``Decimal('0.1000000000000000055511151231257827021181583404541015625')``.
   (Implemented by Raymond Hettinger; :issue:`4796`.)
 
+* The :class:`Fraction` class will now accept two rational numbers
+  as arguments to its constructor.
+  (Implemented by Mark Dickinson; :issue:`5812`.)
+
 * New function: the :mod:`gc` module's :func:`is_tracked` returns
   true if a given instance is tracked by the garbage collector, false
   otherwise. (Contributed by Antoine Pitrou; :issue:`4688`.)
@@ -419,6 +452,13 @@
 
   (Implemented by Antoine Pitrou; :issue:`4444`.)
 
+  The methods :meth:`addCleanup` and :meth:`doCleanups` were added.
+  :meth:`addCleanup` allows you to add cleanup functions that
+  will be called unconditionally (after :meth:`setUp` if
+  :meth:`setUp` fails, otherwise after :meth:`tearDown`). This allows
+  for much simpler resource allocation and deallocation during tests.
+  :issue:`5679`
+
   A number of new methods were added that provide more specialized
   tests.  Many of these methods were written by Google engineers
   for use in their test suites; Gregory P. Smith, Michael Foord, and
@@ -473,6 +513,14 @@
     to provide additional information about why the two objects are
     matching, much as the new sequence comparison methods do.
 
+  :func:`unittest.main` now takes an optional ``exit`` argument.
+  If False ``main`` doesn't call :func:`sys.exit` allowing it to
+  be used from the interactive interpreter. :issue:`3379`.
+
+  :class:`TestResult` has new :meth:`startTestRun` and
+  :meth:`stopTestRun` methods; called immediately before
+  and after a test run. :issue:`5728` by Robert Collins.
+
 * The :func:`is_zipfile` function in the :mod:`zipfile` module will now
   accept a file object, in addition to the path names accepted in earlier
   versions.  (Contributed by Gabriel Genellina; :issue:`4756`.)
@@ -553,6 +601,10 @@
   is particularly useful for asynchronous IO operations.
   (Contributed by Kristjan Valur Jonsson; :issue:`4293`.)
 
+* Global symbols defined by the :mod:`ctypes` module are now prefixed
+  with ``Py`, or with ``_ctypes``.  (Implemented by Thomas
+  Heller; :issue:`3102`.)
+
 * The :program:`configure` script now checks for floating-point rounding bugs
   on certain 32-bit Intel chips and defines a :cmacro:`X87_DOUBLE_ROUNDING`
   preprocessor definition.  No code currently uses this definition,
@@ -591,10 +643,10 @@
 
 * When importing a module from a :file:`.pyc` or :file:`.pyo` file
   with an existing :file:`.py` counterpart, the :attr:`co_filename`
-  attributes of all code objects if the original filename is obsolete,
-  which can happen if the file has been renamed, moved, or is accessed
-  through different paths.  (Patch by Ziga Seilnacht and Jean-Paul
-  Calderone; :issue:`1180193`.)
+  attributes of the resulting code objects are overwritten when the
+  original filename is obsolete.  This can happen if the file has been
+  renamed, moved, or is accessed through different paths.  (Patch by
+  Ziga Seilnacht and Jean-Paul Calderone; :issue:`1180193`.)
 
 * The :file:`regrtest.py` script now takes a :option:`--randseed=`
   switch that takes an integer that will be used as the random seed

Modified: python/branches/py3k/Lib/collections.py
==============================================================================
--- python/branches/py3k/Lib/collections.py	(original)
+++ python/branches/py3k/Lib/collections.py	Fri May  8 22:42:26 2009
@@ -271,9 +271,12 @@
 
     # For pickling to work, the __module__ variable needs to be set to the frame
     # where the named tuple is created.  Bypass this step in enviroments where
-    # sys._getframe is not defined (Jython for example).
-    if hasattr(_sys, '_getframe'):
+    # sys._getframe is not defined (Jython for example) or sys._getframe is not
+    # defined for arguments greater than 0 (IronPython).
+    try:
         result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
+    except (AttributeError, ValueError):
+        pass
 
     return result
 

Modified: python/branches/py3k/Lib/logging/__init__.py
==============================================================================
--- python/branches/py3k/Lib/logging/__init__.py	(original)
+++ python/branches/py3k/Lib/logging/__init__.py	Fri May  8 22:42:26 2009
@@ -756,7 +756,7 @@
         The record is then written to the stream with a trailing newline.  If
         exception information is present, it is formatted using
         traceback.print_exception and appended to the stream.  If the stream
-        has an 'encoding' attribute, it is used to encode the message before
+        has an 'encoding' attribute, it is used to determine how to do the
         output to the stream.
         """
         try:
@@ -767,11 +767,21 @@
                 stream.write(fs % msg)
             else:
                 try:
-                    if (isinstance(msg, unicode) or
-                        getattr(stream, 'encoding', None) is None):
-                        stream.write(fs % msg)
+                    if (isinstance(msg, unicode) and
+                        getattr(stream, 'encoding', None)):
+                        fs = fs.decode(stream.encoding)
+                        try:
+                            stream.write(fs % msg)
+                        except UnicodeEncodeError:
+                            #Printing to terminals sometimes fails. For example,
+                            #with an encoding of 'cp1251', the above write will
+                            #work if written to a stream opened or wrapped by
+                            #the codecs module, but fail when writing to a
+                            #terminal even when the codepage is set to cp1251.
+                            #An extra encoding step seems to be needed.
+                            stream.write((fs % msg).encode(stream.encoding))
                     else:
-                        stream.write(fs % msg.encode(stream.encoding))
+                        stream.write(fs % msg)
                 except UnicodeError:
                     stream.write(fs % msg.encode("UTF-8"))
             self.flush()

Modified: python/branches/py3k/Lib/test/regrtest.py
==============================================================================
--- python/branches/py3k/Lib/test/regrtest.py	(original)
+++ python/branches/py3k/Lib/test/regrtest.py	Fri May  8 22:42:26 2009
@@ -663,6 +663,7 @@
 
 def cleanup_test_droppings(testname, verbose):
     import shutil
+    import stat
 
     # Try to clean up junk commonly left behind.  While tests shouldn't leave
     # any files or directories behind, when a test fails that can be tedious
@@ -687,6 +688,10 @@
         if verbose:
             print("%r left behind %s %r" % (testname, kind, name))
         try:
+            # if we have chmod, fix possible permissions problems
+            # that might prevent cleanup
+            if (hasattr(os, 'chmod')):
+                os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
             nuker(name)
         except Exception as msg:
             print(("%r left behind %s %r and it couldn't be "

Modified: python/branches/py3k/Lib/test/test_aifc.py
==============================================================================
--- python/branches/py3k/Lib/test/test_aifc.py	(original)
+++ python/branches/py3k/Lib/test/test_aifc.py	Fri May  8 22:42:26 2009
@@ -27,7 +27,7 @@
     def test_skipunknown(self):
         #Issue 2245
         #This file contains chunk types aifc doesn't recognize.
-        f = aifc.open(self.sndfilepath)
+        self.f = aifc.open(self.sndfilepath)
 
     def test_params(self):
         f = self.f = aifc.open(self.sndfilepath)

Modified: python/branches/py3k/Lib/test/test_descr.py
==============================================================================
--- python/branches/py3k/Lib/test/test_descr.py	(original)
+++ python/branches/py3k/Lib/test/test_descr.py	Fri May  8 22:42:26 2009
@@ -3585,31 +3585,6 @@
         self.assertEqual(e.a, 2)
         self.assertEqual(C2.__subclasses__(), [D])
 
-        # stuff that shouldn't:
-        class L(list):
-            pass
-
-        try:
-            L.__bases__ = (dict,)
-        except TypeError:
-            pass
-        else:
-            self.fail("shouldn't turn list subclass into dict subclass")
-
-        try:
-            list.__bases__ = (dict,)
-        except TypeError:
-            pass
-        else:
-            self.fail("shouldn't be able to assign to list.__bases__")
-
-        try:
-            D.__bases__ = (C2, list)
-        except TypeError:
-            pass
-        else:
-            assert 0, "best_base calculation found wanting"
-
         try:
             del D.__bases__
         except (TypeError, AttributeError):
@@ -3657,6 +3632,36 @@
             if tp is not object:
                 self.assertEqual(len(tp.__bases__), 1, tp)
 
+        class L(list):
+            pass
+
+        class C(object):
+            pass
+
+        class D(C):
+            pass
+
+        try:
+            L.__bases__ = (dict,)
+        except TypeError:
+            pass
+        else:
+            self.fail("shouldn't turn list subclass into dict subclass")
+
+        try:
+            list.__bases__ = (dict,)
+        except TypeError:
+            pass
+        else:
+            self.fail("shouldn't be able to assign to list.__bases__")
+
+        try:
+            D.__bases__ = (C, list)
+        except TypeError:
+            pass
+        else:
+            assert 0, "best_base calculation found wanting"
+
 
     def test_mutable_bases_with_failing_mro(self):
         # Testing mutable bases with failing mro...

Modified: python/branches/py3k/Lib/test/test_logging.py
==============================================================================
--- python/branches/py3k/Lib/test/test_logging.py	(original)
+++ python/branches/py3k/Lib/test/test_logging.py	Fri May  8 22:42:26 2009
@@ -894,6 +894,7 @@
         message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
         #Ensure it's written in a Cyrillic encoding
         writer_class = codecs.getwriter('cp1251')
+        writer_class.encoding = 'cp1251'
         stream = io.BytesIO()
         writer = writer_class(stream, 'strict')
         handler = logging.StreamHandler(writer)

Modified: python/branches/py3k/Lib/test/test_shutil.py
==============================================================================
--- python/branches/py3k/Lib/test/test_shutil.py	(original)
+++ python/branches/py3k/Lib/test/test_shutil.py	Fri May  8 22:42:26 2009
@@ -46,9 +46,23 @@
             shutil.rmtree(TESTFN)
 
     def check_args_to_onerror(self, func, arg, exc):
+        # test_rmtree_errors deliberately runs rmtree
+        # on a directory that is chmod 400, which will fail.
+        # This function is run when shutil.rmtree fails.
+        # 99.9% of the time it initially fails to remove
+        # a file in the directory, so the first time through
+        # func is os.remove.
+        # However, some Linux machines running ZFS on
+        # FUSE experienced a failure earlier in the process
+        # at os.listdir.  The first failure may legally
+        # be either.
         if self.errorState == 0:
-            self.assertEqual(func, os.remove)
-            self.assertEqual(arg, self.childpath)
+            if func is os.remove:
+                self.assertEqual(arg, self.childpath)
+            else:
+                self.assertIs(func, os.listdir,
+                              "func must be either os.remove or os.listdir")
+                self.assertEqual(arg, TESTFN)
             self.failUnless(issubclass(exc[0], OSError))
             self.errorState = 1
         else:

Modified: python/branches/py3k/Lib/test/test_unittest.py
==============================================================================
--- python/branches/py3k/Lib/test/test_unittest.py	(original)
+++ python/branches/py3k/Lib/test/test_unittest.py	Fri May  8 22:42:26 2009
@@ -9,9 +9,10 @@
 import re
 from test import support
 import unittest
-from unittest import TestCase
+from unittest import TestCase, TestProgram
 import types
 from copy import deepcopy
+import io
 
 ### Support code
 ################################################################
@@ -25,10 +26,18 @@
         self._events.append('startTest')
         super().startTest(test)
 
+    def startTestRun(self):
+        self._events.append('startTestRun')
+        super(LoggingResult, self).startTestRun()
+
     def stopTest(self, test):
         self._events.append('stopTest')
         super().stopTest(test)
 
+    def stopTestRun(self):
+        self._events.append('stopTestRun')
+        super(LoggingResult, self).stopTestRun()
+
     def addFailure(self, *args):
         self._events.append('addFailure')
         super().addFailure(*args)
@@ -1826,6 +1835,12 @@
         self.assertEqual(result.testsRun, 1)
         self.assertEqual(result.shouldStop, False)
 
+    # "Called before and after tests are run. The default implementation does nothing."
+    def test_startTestRun_stopTestRun(self):
+        result = unittest.TestResult()
+        result.startTestRun()
+        result.stopTestRun()
+
     # "addSuccess(test)"
     # ...
     # "Called when the test case test succeeds"
@@ -1973,6 +1988,53 @@
 class Bar(Foo):
     def test2(self): pass
 
+class LoggingTestCase(unittest.TestCase):
+    """A test case which logs its calls."""
+
+    def __init__(self, events):
+        super(LoggingTestCase, self).__init__('test')
+        self.events = events
+
+    def setUp(self):
+        self.events.append('setUp')
+
+    def test(self):
+        self.events.append('test')
+
+    def tearDown(self):
+        self.events.append('tearDown')
+
+class ResultWithNoStartTestRunStopTestRun(object):
+    """An object honouring TestResult before startTestRun/stopTestRun."""
+
+    def __init__(self):
+        self.failures = []
+        self.errors = []
+        self.testsRun = 0
+        self.skipped = []
+        self.expectedFailures = []
+        self.unexpectedSuccesses = []
+        self.shouldStop = False
+
+    def startTest(self, test):
+        pass
+
+    def stopTest(self, test):
+        pass
+
+    def addError(self, test):
+        pass
+
+    def addFailure(self, test):
+        pass
+
+    def addSuccess(self, test):
+        pass
+
+    def wasSuccessful(self):
+        return True
+
+
 ################################################################
 ### /Support code for Test_TestCase
 
@@ -2067,19 +2129,30 @@
         events = []
         result = LoggingResult(events)
 
-        class Foo(unittest.TestCase):
+        class Foo(LoggingTestCase):
             def setUp(self):
-                events.append('setUp')
+                super(Foo, self).setUp()
                 raise RuntimeError('raised by Foo.setUp')
 
-            def test(self):
-                events.append('test')
+        Foo(events).run(result)
+        expected = ['startTest', 'setUp', 'addError', 'stopTest']
+        self.assertEqual(events, expected)
 
-            def tearDown(self):
-                events.append('tearDown')
+    # "With a temporary result stopTestRun is called when setUp errors.
+    def test_run_call_order__error_in_setUp_default_result(self):
+        events = []
 
-        Foo('test').run(result)
-        expected = ['startTest', 'setUp', 'addError', 'stopTest']
+        class Foo(LoggingTestCase):
+            def defaultTestResult(self):
+                return LoggingResult(self.events)
+
+            def setUp(self):
+                super(Foo, self).setUp()
+                raise RuntimeError('raised by Foo.setUp')
+
+        Foo(events).run()
+        expected = ['startTestRun', 'startTest', 'setUp', 'addError',
+                    'stopTest', 'stopTestRun']
         self.assertEqual(events, expected)
 
     # "When a setUp() method is defined, the test runner will run that method
@@ -2093,20 +2166,32 @@
         events = []
         result = LoggingResult(events)
 
-        class Foo(unittest.TestCase):
-            def setUp(self):
-                events.append('setUp')
-
+        class Foo(LoggingTestCase):
             def test(self):
-                events.append('test')
+                super(Foo, self).test()
                 raise RuntimeError('raised by Foo.test')
 
-            def tearDown(self):
-                events.append('tearDown')
-
         expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown',
                     'stopTest']
-        Foo('test').run(result)
+        Foo(events).run(result)
+        self.assertEqual(events, expected)
+
+    # "With a default result, an error in the test still results in stopTestRun
+    # being called."
+    def test_run_call_order__error_in_test_default_result(self):
+        events = []
+
+        class Foo(LoggingTestCase):
+            def defaultTestResult(self):
+                return LoggingResult(self.events)
+
+            def test(self):
+                super(Foo, self).test()
+                raise RuntimeError('raised by Foo.test')
+
+        expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addError',
+                    'tearDown', 'stopTest', 'stopTestRun']
+        Foo(events).run()
         self.assertEqual(events, expected)
 
     # "When a setUp() method is defined, the test runner will run that method
@@ -2120,20 +2205,30 @@
         events = []
         result = LoggingResult(events)
 
-        class Foo(unittest.TestCase):
-            def setUp(self):
-                events.append('setUp')
-
+        class Foo(LoggingTestCase):
             def test(self):
-                events.append('test')
+                super(Foo, self).test()
                 self.fail('raised by Foo.test')
 
-            def tearDown(self):
-                events.append('tearDown')
-
         expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown',
                     'stopTest']
-        Foo('test').run(result)
+        Foo(events).run(result)
+        self.assertEqual(events, expected)
+
+    # "When a test fails with a default result stopTestRun is still called."
+    def test_run_call_order__failure_in_test_default_result(self):
+
+        class Foo(LoggingTestCase):
+            def defaultTestResult(self):
+                return LoggingResult(self.events)
+            def test(self):
+                super(Foo, self).test()
+                self.fail('raised by Foo.test')
+
+        expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addFailure',
+                    'tearDown', 'stopTest', 'stopTestRun']
+        events = []
+        Foo(events).run()
         self.assertEqual(events, expected)
 
     # "When a setUp() method is defined, the test runner will run that method
@@ -2147,22 +2242,44 @@
         events = []
         result = LoggingResult(events)
 
-        class Foo(unittest.TestCase):
-            def setUp(self):
-                events.append('setUp')
-
-            def test(self):
-                events.append('test')
-
+        class Foo(LoggingTestCase):
             def tearDown(self):
-                events.append('tearDown')
+                super(Foo, self).tearDown()
                 raise RuntimeError('raised by Foo.tearDown')
 
-        Foo('test').run(result)
+        Foo(events).run(result)
         expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError',
                     'stopTest']
         self.assertEqual(events, expected)
 
+    # "When tearDown errors with a default result stopTestRun is still called."
+    def test_run_call_order__error_in_tearDown_default_result(self):
+
+        class Foo(LoggingTestCase):
+            def defaultTestResult(self):
+                return LoggingResult(self.events)
+            def tearDown(self):
+                super(Foo, self).tearDown()
+                raise RuntimeError('raised by Foo.tearDown')
+
+        events = []
+        Foo(events).run()
+        expected = ['startTestRun', 'startTest', 'setUp', 'test', 'tearDown',
+                    'addError', 'stopTest', 'stopTestRun']
+        self.assertEqual(events, expected)
+
+    # "TestCase.run() still works when the defaultTestResult is a TestResult
+    # that does not support startTestRun and stopTestRun.
+    def test_run_call_order_default_result(self):
+
+        class Foo(unittest.TestCase):
+            def defaultTestResult(self):
+                return ResultWithNoStartTestRunStopTestRun()
+            def test(self):
+                pass
+
+        Foo('test').run()
+
     # "This class attribute gives the exception raised by the test() method.
     # If a test framework needs to use a specialized exception, possibly to
     # carry additional information, it must subclass this exception in
@@ -2253,7 +2370,9 @@
         self.failUnless(isinstance(Foo().id(), str))
 
     # "If result is omitted or None, a temporary result object is created
-    # and used, but is not made available to the caller"
+    # and used, but is not made available to the caller. As TestCase owns the
+    # temporary result startTestRun and stopTestRun are called.
+
     def test_run__uses_defaultTestResult(self):
         events = []
 
@@ -2267,7 +2386,8 @@
         # Make run() find a result object on its own
         Foo('test').run()
 
-        expected = ['startTest', 'test', 'addSuccess', 'stopTest']
+        expected = ['startTestRun', 'startTest', 'test', 'addSuccess',
+            'stopTest', 'stopTestRun']
         self.assertEqual(events, expected)
 
     def testShortDescriptionWithoutDocstring(self):
@@ -3012,6 +3132,220 @@
                              "^unexpectedly identical: None : oops$"])
 
 
+class TestCleanUp(TestCase):
+
+    def testCleanUp(self):
+        class TestableTest(TestCase):
+            def testNothing(self):
+                pass
+
+        test = TestableTest('testNothing')
+        self.assertEqual(test._cleanups, [])
+
+        cleanups = []
+
+        def cleanup1(*args, **kwargs):
+            cleanups.append((1, args, kwargs))
+
+        def cleanup2(*args, **kwargs):
+            cleanups.append((2, args, kwargs))
+
+        test.addCleanup(cleanup1, 1, 2, 3, four='hello', five='goodbye')
+        test.addCleanup(cleanup2)
+
+        self.assertEqual(test._cleanups,
+                         [(cleanup1, (1, 2, 3), dict(four='hello', five='goodbye')),
+                          (cleanup2, (), {})])
+
+        result = test.doCleanups()
+        self.assertTrue(result)
+
+        self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))])
+
+    def testCleanUpWithErrors(self):
+        class TestableTest(TestCase):
+            def testNothing(self):
+                pass
+
+        class MockResult(object):
+            errors = []
+            def addError(self, test, exc_info):
+                self.errors.append((test, exc_info))
+
+        result = MockResult()
+        test = TestableTest('testNothing')
+        test._result = result
+
+        exc1 = Exception('foo')
+        exc2 = Exception('bar')
+        def cleanup1():
+            raise exc1
+
+        def cleanup2():
+            raise exc2
+
+        test.addCleanup(cleanup1)
+        test.addCleanup(cleanup2)
+
+        self.assertFalse(test.doCleanups())
+
+        (test1, (Type1, instance1, _)), (test2, (Type2, instance2, _)) = reversed(MockResult.errors)
+        self.assertEqual((test1, Type1, instance1), (test, Exception, exc1))
+        self.assertEqual((test2, Type2, instance2), (test, Exception, exc2))
+
+    def testCleanupInRun(self):
+        blowUp = False
+        ordering = []
+
+        class TestableTest(TestCase):
+            def setUp(self):
+                ordering.append('setUp')
+                if blowUp:
+                    raise Exception('foo')
+
+            def testNothing(self):
+                ordering.append('test')
+
+            def tearDown(self):
+                ordering.append('tearDown')
+
+        test = TestableTest('testNothing')
+
+        def cleanup1():
+            ordering.append('cleanup1')
+        def cleanup2():
+            ordering.append('cleanup2')
+        test.addCleanup(cleanup1)
+        test.addCleanup(cleanup2)
+
+        def success(some_test):
+            self.assertEqual(some_test, test)
+            ordering.append('success')
+
+        result = unittest.TestResult()
+        result.addSuccess = success
+
+        test.run(result)
+        self.assertEqual(ordering, ['setUp', 'test', 'tearDown',
+                                    'cleanup2', 'cleanup1', 'success'])
+
+        blowUp = True
+        ordering = []
+        test = TestableTest('testNothing')
+        test.addCleanup(cleanup1)
+        test.run(result)
+        self.assertEqual(ordering, ['setUp', 'cleanup1'])
+
+
+class Test_TestProgram(TestCase):
+
+    # Horrible white box test
+    def testNoExit(self):
+        result = object()
+        test = object()
+
+        class FakeRunner(object):
+            def run(self, test):
+                self.test = test
+                return result
+
+        runner = FakeRunner()
+
+        try:
+            oldParseArgs = TestProgram.parseArgs
+            TestProgram.parseArgs = lambda *args: None
+            TestProgram.test = test
+
+            program = TestProgram(testRunner=runner, exit=False)
+
+            self.assertEqual(program.result, result)
+            self.assertEqual(runner.test, test)
+
+        finally:
+            TestProgram.parseArgs = oldParseArgs
+            del TestProgram.test
+
+
+    class FooBar(unittest.TestCase):
+        def testPass(self):
+            assert True
+        def testFail(self):
+            assert False
+
+    class FooBarLoader(unittest.TestLoader):
+        """Test loader that returns a suite containing FooBar."""
+        def loadTestsFromModule(self, module):
+            return self.suiteClass(
+                [self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
+
+
+    def test_NonExit(self):
+        program = unittest.main(exit=False,
+                                argv=["foobar"],
+                                testRunner=unittest.TextTestRunner(stream=io.StringIO()),
+                                testLoader=self.FooBarLoader())
+        self.assertTrue(hasattr(program, 'result'))
+
+
+    def test_Exit(self):
+        self.assertRaises(
+            SystemExit,
+            unittest.main,
+            argv=["foobar"],
+            testRunner=unittest.TextTestRunner(stream=io.StringIO()),
+            exit=True,
+            testLoader=self.FooBarLoader())
+
+
+    def test_ExitAsDefault(self):
+        self.assertRaises(
+            SystemExit,
+            unittest.main,
+            argv=["foobar"],
+            testRunner=unittest.TextTestRunner(stream=io.StringIO()),
+            testLoader=self.FooBarLoader())
+
+
+class Test_TextTestRunner(TestCase):
+    """Tests for TextTestRunner."""
+
+    def test_works_with_result_without_startTestRun_stopTestRun(self):
+        class OldTextResult(ResultWithNoStartTestRunStopTestRun):
+            separator2 = ''
+            def printErrors(self):
+                pass
+
+        class Runner(unittest.TextTestRunner):
+            def __init__(self):
+                super(Runner, self).__init__(io.StringIO())
+
+            def _makeResult(self):
+                return OldTextResult()
+
+        runner = Runner()
+        runner.run(unittest.TestSuite())
+
+    def test_startTestRun_stopTestRun_called(self):
+        class LoggingTextResult(LoggingResult):
+            separator2 = ''
+            def printErrors(self):
+                pass
+
+        class LoggingRunner(unittest.TextTestRunner):
+            def __init__(self, events):
+                super(LoggingRunner, self).__init__(io.StringIO())
+                self._events = events
+
+            def _makeResult(self):
+                return LoggingTextResult(self._events)
+
+        events = []
+        runner = LoggingRunner(events)
+        runner.run(unittest.TestSuite())
+        expected = ['startTestRun', 'stopTestRun']
+        self.assertEqual(events, expected)
+
+
 ######################################################################
 ## Main
 ######################################################################
@@ -3019,7 +3353,8 @@
 def test_main():
     support.run_unittest(Test_TestCase, Test_TestLoader,
         Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
-        Test_TestSkipping, Test_Assertions, TestLongMessage)
+        Test_TestSkipping, Test_Assertions, TestLongMessage,
+        Test_TestProgram, TestCleanUp)
 
 if __name__ == "__main__":
     test_main()

Modified: python/branches/py3k/Lib/unittest.py
==============================================================================
--- python/branches/py3k/Lib/unittest.py	(original)
+++ python/branches/py3k/Lib/unittest.py	Fri May  8 22:42:26 2009
@@ -173,10 +173,22 @@
         "Called when the given test is about to be run"
         self.testsRun = self.testsRun + 1
 
+    def startTestRun(self):
+        """Called once before any tests are executed.
+
+        See startTest for a method called before each test.
+        """
+
     def stopTest(self, test):
         "Called when the given test has been run"
         pass
 
+    def stopTestRun(self):
+        """Called once after all tests are executed.
+
+        See stopTest for a method called after each test.
+        """
+
     def addError(self, test, err):
         """Called when an error has occurred. 'err' is a tuple of values as
         returned by sys.exc_info().
@@ -262,7 +274,7 @@
     def __enter__(self):
         pass
 
-    def __exit__(self, exc_type, exc_value, traceback):
+    def __exit__(self, exc_type, exc_value, tb):
         if exc_type is None:
             try:
                 exc_name = self.expected.__name__
@@ -341,12 +353,14 @@
            not have a method with the specified name.
         """
         self._testMethodName = methodName
+        self._result = None
         try:
             testMethod = getattr(self, methodName)
         except AttributeError:
             raise ValueError("no such test method in %s: %s" % \
                   (self.__class__, methodName))
         self._testMethodDoc = testMethod.__doc__
+        self._cleanups = []
 
         # Map types to custom assertEqual functions that will compare
         # instances of said type in more detail to generate a more useful
@@ -373,6 +387,14 @@
         """
         self._type_equality_funcs[typeobj] = _AssertWrapper(function)
 
+    def addCleanup(self, function, *args, **kwargs):
+        """Add a function, with arguments, to be called when the test is
+        completed. Functions added are called on a LIFO basis and are
+        called after tearDown on test failure or success.
+
+        Cleanup items are called even if setUp fails (unlike tearDown)."""
+        self._cleanups.append((function, args, kwargs))
+
     def setUp(self):
         "Hook method for setting up the test fixture before exercising it."
         pass
@@ -428,45 +450,70 @@
                (_strclass(self.__class__), self._testMethodName)
 
     def run(self, result=None):
+        orig_result = result
         if result is None:
             result = self.defaultTestResult()
+            startTestRun = getattr(result, 'startTestRun', None)
+            if startTestRun is not None:
+                startTestRun()
+
+        self._result = result
         result.startTest(self)
         testMethod = getattr(self, self._testMethodName)
         try:
-            try:
-                self.setUp()
-            except SkipTest as e:
-                result.addSkip(self, str(e))
-                return
-            except Exception:
-                result.addError(self, sys.exc_info())
-                return
-
             success = False
             try:
-                testMethod()
-            except self.failureException:
-                result.addFailure(self, sys.exc_info())
-            except _ExpectedFailure as e:
-                result.addExpectedFailure(self, e.exc_info)
-            except _UnexpectedSuccess:
-                result.addUnexpectedSuccess(self)
+                self.setUp()
             except SkipTest as e:
                 result.addSkip(self, str(e))
             except Exception:
                 result.addError(self, sys.exc_info())
             else:
-                success = True
+                try:
+                    testMethod()
+                except self.failureException:
+                    result.addFailure(self, sys.exc_info())
+                except _ExpectedFailure as e:
+                    result.addExpectedFailure(self, e.exc_info)
+                except _UnexpectedSuccess:
+                    result.addUnexpectedSuccess(self)
+                except SkipTest as e:
+                    result.addSkip(self, str(e))
+                except Exception:
+                    result.addError(self, sys.exc_info())
+                else:
+                    success = True
 
-            try:
-                self.tearDown()
-            except Exception:
-                result.addError(self, sys.exc_info())
-                success = False
+                try:
+                    self.tearDown()
+                except Exception:
+                    result.addError(self, sys.exc_info())
+                    success = False
+
+            cleanUpSuccess = self.doCleanups()
+            success = success and cleanUpSuccess
             if success:
                 result.addSuccess(self)
         finally:
             result.stopTest(self)
+            if orig_result is None:
+                stopTestRun = getattr(result, 'stopTestRun', None)
+                if stopTestRun is not None:
+                    stopTestRun()
+
+    def doCleanups(self):
+        """Execute all cleanup functions. Normally called for you after
+        tearDown."""
+        result = self._result
+        ok = True
+        while self._cleanups:
+            function, args, kwargs = self._cleanups.pop(-1)
+            try:
+                function(*args, **kwargs)
+            except Exception:
+                ok = False
+                result.addError(self, sys.exc_info())
+        return ok
 
     def __call__(self, *args, **kwds):
         return self.run(*args, **kwds)
@@ -1037,7 +1084,7 @@
     def __eq__(self, other):
         if not isinstance(other, self.__class__):
             return NotImplemented
-        return self._tests == other._tests
+        return list(self) == list(other)
 
     def __ne__(self, other):
         return not self == other
@@ -1160,8 +1207,7 @@
                      self._testFunc, self._description))
 
     def __str__(self):
-        return "%s (%s)" % (_strclass(self.__class__),
-                            self.__testFunc.__name__)
+        return "%s (%s)" % (_strclass(self.__class__), self._testFunc.__name__)
 
     def __repr__(self):
         return "<%s testFunc=%s>" % (_strclass(self.__class__), self._testFunc)
@@ -1449,7 +1495,15 @@
         "Run the given test case or test suite."
         result = self._makeResult()
         startTime = time.time()
-        test(result)
+        startTestRun = getattr(result, 'startTestRun', None)
+        if startTestRun is not None:
+            startTestRun()
+        try:
+            test(result)
+        finally:
+            stopTestRun = getattr(result, 'stopTestRun', None)
+            if stopTestRun is not None:
+                stopTestRun()
         stopTime = time.time()
         timeTaken = stopTime - startTime
         result.printErrors()
@@ -1511,7 +1565,7 @@
 """
     def __init__(self, module='__main__', defaultTest=None,
                  argv=None, testRunner=TextTestRunner,
-                 testLoader=defaultTestLoader):
+                 testLoader=defaultTestLoader, exit=True):
         if isinstance(module, str):
             self.module = __import__(module)
             for part in module.split('.')[1:]:
@@ -1520,6 +1574,8 @@
             self.module = module
         if argv is None:
             argv = sys.argv
+
+        self.exit = exit
         self.verbosity = 1
         self.defaultTest = defaultTest
         self.testRunner = testRunner
@@ -1571,8 +1627,9 @@
         else:
             # it is assumed to be a TestRunner instance
             testRunner = self.testRunner
-        result = testRunner.run(self.test)
-        sys.exit(not result.wasSuccessful())
+        self.result = testRunner.run(self.test)
+        if self.exit:
+            sys.exit(not self.result.wasSuccessful())
 
 main = TestProgram
 


More information about the Python-checkins mailing list