Python Programming, news on the Voidspace Python Projects and all things techie.
A plugin system for unittest(2)
One of the big problems with unittest, which I have been maintaining for well over a year now, is how hard it is to extend. For a long time I've been saying that my "big task" with unittest was to implement a plugin system. Well, at the EuroPython sprints I finally got round to making a start.
There is now a prototype plugin system for unittest2 in the plugins branch of the mercurial repository. You can try it out with:
hg clone http://hg.python.org/unittest2cd unittest2hg up pluginspython setup.py install
I'm looking for feedback on the system, and have written up a rather lengthy "proto-pep" that describes the whole system. Most of the document describes the specific extension points, so you can get a good feel for it from just reading the first few sections. I may rewrite it as an official PEP, as my goal is to get this into unittest for Python 3.2, but at the moment it lives in the repository or can be found online at:
http://hg.python.org/unittest2/raw-file/tip/description.txt
As part of the prototype I have been implementing some example plugins (in unittest2/plugins/) so I can develop the mechanism against real rather than imagined use cases. Jason Pellerin, creator of nose, has been providing me with feedback and has been trying it out by porting some of the nose plugins to unittest [1]. His initial comment on the system was "it looks very flexible and clean".
One of the goals of the extension mechanism is to allow nose2 to be a much thinner set of plugins over unittest(2), making nose2 simpler to maintain and less likely to break every time unittest changes [2].
Example plugins available and included:
- a pep8 and pyflakes checker
- a debugger plugin that drops you into pdb on test fail / error
- a doctest loader (looks for doctests in all text files in the project)
- use a regex for matching files in test discovery instead of a glob
- growl notifications on test run start and stop
- filter individual test methods using a regex
- load test functions from modules as well as TestCases
- integration with the coverage module for coverage reporting
In addition I intend to create a plugin that outputs junit compatible xml from a test run (for integration with tools like the hudson continuous integration server) and a test runner that runs tests in parallel using multiprocessing.
Not all of these will be included in the merge to unittest. Which ones will is still an open question.
I'd like feedback on the proposal, and hopefully approval to port it into unittest after discussion / amendment / completion. In particular I'd like feedback on the basic system, plus which events should be available and what information should be available in them. Note that the system is not complete in the prototype. Enough is implemented to get "the general idea" and to formalise the full system. It still needs extensive tests and the extra work in TestProgram makes it abundantly clear that refactoring there is well overdue...
So, give it a try, find bugs in it, write plugins with it and let me know what you think...
Here's an example of a unittest plugin. This one is the "debugger" plugin that drops you into pdb on test fail or error (configurable):
from unittest2 import Plugin
import pdb
import sys
class Debugger(Plugin):
configSection = 'debugger'
commandLineSwitch = ('D', 'debugger', 'Enter pdb on test fail or error')
def __init__(self):
self.errorsOnly = self.config.as_bool('errors-only', default=False)
def onTestFail(self, event):
value, tb = event.exc_info[1:]
test = event.test
if self.errorsOnly and isinstance(value, test.failureException):
return
original = sys.stdout
sys.stdout = sys.__stdout__
try:
pdb.post_mortem(tb)
finally:
sys.stdout = original
The plugin is hooked into the command line interface of the unit2 test runner and the plugin configuration files through the configSection and commandLineSwitch class attributes. There are APIs that give complete control over this, but the Plugin class provides some nice sugar for making writing plugins simpler. See the description document for more details than you could possibly want...
To get started you'll need a unittest.cfg file in either your home directory or the same directory you run unit2 from. There is an example config file, with all the plugins configured, in the unittest2 repository. Once you have that in place unit2 -h will show you command line options for all the configured plugins:
--checker Check all Python files with pep8 and pyflakes
--test-functions Load test functions from test modules
--doctest Load doctests from text files
-C, --coverage Enable coverage reporting
--cover-module=COVER-MODULE
Specify a module or package for coverage
reporting
-G, --growl Growl notifications on test run start and stop
-F FILTER, --filter=FILTER
Filter test methods loaded with a regexp
-D, --debugger Enter pdb on test fail or error
-R, --match-regexp Match filenames during test discovery with
regular expressions instead of glob
Most of the plugins can be configured in the config file(s) too. Looking at the options there and poking around in the source code of the plugins should show you everything you need to know...
| [1] | See http://bitbucket.org/jpellerin/unittest2/src/tip/unittest2/plugins/attrib.py and http://bitbucket.org/jpellerin/unittest2/src/tip/unittest2/plugins/errormaster.py |
| [2] | http://lists.idyll.org/pipermail/testing-in-python/2010-March/002799.html |
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-07-30 13:15:19 | |
Categories: Projects, Python, Hacking Tags: unittest, testing, plugins
unittest2 0.5.0: setuptools compatible test collector and Python 2.3 distribution
unittest2 0.5.0 has just been released. This version of unittest2 has "feature parity" with the version of unittest in Python 2.7:
If you want to ensure that your tests run identically under unittest2 and unittest in Python 2.7 you should use unittest2 0.5.0. Later versions of unittest2 will include changes in unittest made in Python 3.2 and onwards after the release of Python 2.7.
unittest2 is a backport of the recent enhancements in the Python unittest testing library in Python 2.7.
Improvements in unittest2 over standard unittest in Python 2.6 and earlier include:
- automatic test discovery from the command line
- failfast, catch and buffer command line options
- class and module level setup and teardowns
- addCleanup for better resource handling
- test skipping and expected failures
- improvements to assertRaises and assertAlmostEqual
- many new assert methods, plus better failure messages
- load_tests protocol for customizing test loading
- various other API improvements and fixes
unittest2 is tested with Python 2.4 - 2.7. New in 0.5.0 is a distribution for Python 2.3 contributed by Mark Roddy.
Also new in 0.5.0 is a setuptools compatible test collector. If you put test_suite = 'unittest2.collector' in setup.py you can then find and run your tests (using test discovery) with:
python setup.py test
This starts test discovery with the default parameters from the directory containing setup.py, so it is perhaps most useful as an example (see unittest2/collector.py).
All Changes in 0.5.0
Addition of a setuptools compatible test collector (very basic). Specify test_suite = 'unittest2.collector' in your setup.py.
TestSuite.debug() and TestCase.debug() now execute cleanup functions and class and module level setups and teardowns.
No longer monkey-patch os.path.relpath for Python 2.4 / 2.5 so that projects don't accidentally depend on our patching. Contributed by Konrad Delong.
Added a Python version specific unit2 entrypoint. This will, for example, create a unit2-2.6 script if unittest2 is installed with Python 2.6. (Requires setuptools or distribute.)
Python 2.3 compatibility (in the python2.3 branch of the repository), contributed by Mark Roddy.
setuptools console script entry points are created as '.py' scripts on Windows.
Feature parity with the Python 2.7 final release.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-07-11 20:02:03 | |
Categories: Projects, Python Tags: unittest2, release, test, testing
PyCrypto 2.1 for Python 2.7 on Windows
Now that Python 2.7 final is out I've compiled PyCrypto 2.1 for Windows.
The new build is available for download along with the other binaries:
Unfortunately compiling PyCrypto for 64bit Windows (AMD64 architecture) is "non-trivial", so I am unable to provide binary builds.
I've also tried compiling pysco for Python 2.7 (both psyco 1.6 and psyco v2). Unfortunately psyco is not yet compatible with Python 2.7. Christian Tismer is aware of this and will work on it (for psyco 2 at least) as soon as he is able.
Like this post? Digg it or Del.icio.us it.
ContextDecorator: creating APIs that work as decorators and context managers
Two of the best additions to Python in recent years are the with
statement and decorators. Both context managers (objects used in with
statements) and decorators can be used for similar purposes: performing an
action before and after executing the decorated function or the code inside
the with block. In fact I now find that many places I used to use decorators
I now prefer the with statement (if I'm lucky enough to be able to ignore
Python 2.4 compatibility).
If you're a library or framework creator then it is nice to be able to create APIs that can be used either as decorators or context managers. The patch decorators in mock behave like this, and when I was writing a new variant (patch.dict) I found myself having to figure out again how to do it. It isn't hard, but it's a bit fiddly. Nor is this an uncommon pattern, both py.test and Django have code that behaves like this.
I've written a very simple utility class that does this, called ContextDecorator, and it is now part of contextlib in Python 3.2.
Context managers inheriting from ContextDecorator have to implement __enter__ and __exit__ as normal. __exit__ retains its optional exception handling even when used as a decorator.
Even better contextlib.contextmanager, which is a decorator for writing context managers as functions, uses ContextDecorator so the context managers it creates can automatically be used as decorators as well.
I've put both ContextDecorator and the new contextmanager into a package on PyPI, and it works with all versions of Python from 2.4 - 3.1.
Example:
from contextdecorator import ContextDecorator
class mycontext(ContextDecorator):
def __enter__(self):
print 'Starting'
return self
def __exit__(self, *exc):
print 'Finishing'
return False
>>> @mycontext()
... def function():
... print 'The bit in the middle'
...
>>> function()
Starting
The bit in the middle
Finishing
>>> with mycontext():
... print 'The bit in the middle'
...
Starting
The bit in the middle
Finishing
Existing context managers that already have a base class can be extended by using ContextDecorator as a mixin class:
from contextdecorator import ContextDecorator
class mycontext(ContextBaseClass, ContextDecorator):
def __enter__(self):
return self
def __exit__(self, *exc):
return False
contextdecorator also contains an implementation of contextlib.contextmanager that uses ContextDecorator. The context managers it creates can be used as decorators as well as in with statements.
from contextdecorator import contextmanager
@contextmanager
def mycontext(*args):
print 'Started'
try:
# decorated function or with
# statement executed here
yield
finally:
# exception handling here
print 'Finished!'
>>> @mycontext('some', 'args')
... def function():
... print 'In the middle'
...
Started
In the middle
Finished!
>>> with mycontext('some', 'args'):
... print 'In the middle'
...
Started
In the middle
Finished!
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-07-01 00:46:45 | |
Categories: Python, Projects, Hacking Tags: release, contextdecorator, decorators, with statement
Release: mock 0.7 beta 2
I'm pleased to announce a new release of the mock module, the first in a
while. Konrad Delong has joined me as a maintainer of mock and has been
a great help in getting this release out. As there are several major new
features this is a beta release, with 0.7.0 final coming out in a few
weeks assuming there are no major problems discovered. Please download
it and try it out:
mock is a Python module that provides a core Mock class. It is intended to reduce the need for creating a host of trivial stubs throughout your test suite. After performing an action, you can make assertions about which methods / attributes were used and arguments they were called with. You can also specify return values and set needed attributes in the normal way.
The mock module also provides utility functions / objects to assist with testing, particularly monkey patching.
mock is tested on Python versions 2.4-2.7 and Python 3.
Full documentation is included in the distribution.
- PDF documentation for 0.7.0b2
- mock on google code (repository and issue tracker)
- mock documentation
- Article on mocking, patching and stubbing
Mock is very easy to use and is designed for use with unittest. Mock is based on the 'action -> assertion' pattern instead of 'record -> replay' used by many mocking frameworks. See the mock documentation for full details.
Changes in 0.7.0 (including the much awaited magic method support) are:
- Addition of mocksignature
- Ability to mock magic methods
- Ability to use patch and patch.object as class decorators
- Renamed patch_object to patch.object (patch_object is deprecated)
- Addition of MagicMock class with all magic methods pre-created for you
- Python 3 compatibility (tested with 3.2 but should work with 3.0 & 3.1 as well)
- Addition of patch.dict(...) for changing dictionaries during a test
- Addition of mocksignature argument to patch and patch.object
- help(mock) works now (on the module). Can no longer use __bases__ as a valid sentinel name (thanks to Stephen Emslie for reporting and diagnosing this)
- Addition of soft comparisons: call_args, call_args_list and method_calls return now tuple-like objects which compare equal even when empty args or kwargs are skipped
- Added some docstrings.
- BUGFIX: side_effect now works with BaseException exceptions like KeyboardInterrupt
- BUGFIX: patching the same object twice now restores the patches correctly
- The tests now require unittest2 to run
- Konrad Delong added as co-maintainer
There are several major new features in this release, not least of which is the support for mocking the Python protocols (magic methods).
The easiest way of using magic methods is with the MagicMock class. It allows you to do things like:
>>> from mock import MagicMock
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()
Note
In the 0.7.0 final release (and already in svn) using the spec keyword argument to MagicMock will only pre-create the magic methods that are in the spec object or list.
Mock allows you to assign functions (or other Mock instances) to magic methods and they will be called appropriately. The MagicMock class is just a Mock variant that has all of the magic methods pre-created for you (well - all the useful ones anyway).
The following is an example of using magic methods with the ordinary Mock class:
>>> from mock import Mock
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'wheeeeee'
>>> str(mock)
'wheeeeee'
mocksignature is a useful companion to Mock and patch. It creates copies of functions that delegate to a mock, but have the same signature as the original function. This ensures that your mocks will fail in the same way as your production code if they are called incorrectly:
>>> from mock import mocksignature
>>> def function(a, b, c):
... pass
...
>>> function2 = mocksignature(function)
>>> function2.mock.return_value = 'fishy'
>>> function2(1, 2, 3)
'fishy'
>>> function2.mock.assert_called_with(1, 2, 3)
>>> function2('wrong arguments')
Traceback (most recent call last):
...
TypeError: <lambda>() takes exactly 3 arguments (1 given)
Also new is patch.dict for setting values in a dictionary just during a test and restoring the dictionary to its original state when the test ends:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-06-27 15:20:42 | |
Categories: Python, Projects Tags: release, mock, testing, beta
Discover 0.4.0: test discovery for unittest
discover is a backport of the new test discovery features only from Python 2.7 / 3.2. The discover module provides automatic test discovery for standard unittest based tests:
python -m discoverpython discover.py
If you have setuptools or distribute installed you will also have a discover script available.
This will discover all tests (with certain restrictions) from the current directory. The discover module has several options to control its behavior (full usage options are displayed with python -m discover -h).
discover 0.4.0 provides feature parity with the test discovery in Python 2.7 RC1 and unittest2 0.4.2.
unittest2 provides not just the test discovery features that are new in Python 2.7, but a whole lot more as well.
The full list of changes since discover 0.3.2:
- Addition of a setuptools compatible test collector. Set "test_suite = 'discover.collector'" in setup.py. "setup.py test" will start test discovery with default parameters from the same directory as the setup.py.
- Allow test discovery using dotted module names instead of a path.
- Addition of a setuptools compatible entrypoint for the discover script.
- A faulty load_tests function will not halt test discovery. A failing test is created to report the error.
- If test discovery imports a module from the wrong location (usually because the module is globally installed and the user is expecting to run tests against a development version in a different location) then discovery halts with an ImportError and the problem is reported.
- Matching files during test discovery is done in DiscoveringTestLoader._match_path. This method can be overriden in subclasses to, for example, match on the full file path or use regular expressions for matching.
- Tests for discovery ported from unittest2. (The tests require unittest2 to run.)
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-06-27 14:09:30 | |
Categories: Python, Projects Tags: discover, release, testing
Python 2.7 Release Candidate 1 and unittest 0.4.2
Python 2.7 is nearly here. Release candidate 1 is now out. There will probably be a few minor bugfixes before the final release, but now is the time to test your code with Python 2.7.
Despite the language moratorium there are a host of exciting new features in Python 2.7. You can read about them in what's new (which is rather long), or read a summary in my previous blog entry from when beta 2 was released.
The other exciting release from the last few days is unittest2 0.4.2. unittest2 is a backport of the new features in the Python 2.7 version of the standard library testing framework unittest. Version 0.4.2 has feature parity with unittest in Python 2.7 RC 1 and includes a few bug fixes since the last release.
The major improvements over unittest in Python 2.6 include:
- A standard test runner with automatic test discovery
- Improved command line options - fail fast, control-c catching and buffering standard out
- Many new assert methods
- Improvements to assertRaises (as context manager) and assertAlmostEquals (delta keyword argument)
- Class and module level setup and teardown
- Cleanup functions for better resource handling
- Test skipping and expected failures
- Lots of other minor changes and improvements
For more details see:
http://www.voidspace.org.uk/python/articles/unittest2.shtml
Changes since 0.4.1:
2010/06/06 - 0.4.2
Improved help message for unit2 discover -h.
SkipTest in unittest.TestCase.setUpClass or setUpModule is now reported as a skip rather than an error.
Excessively large diffs due to TestCase.assertSequenceEqual are no longer included in failure reports. (Controlled by TestCase.maxDiff.)
Matching files during test discovery is done in TestLoader._match_path. This method can be overriden in subclasses to, for example, match on the full file path or use regular expressions for matching.
Addition of a setuptools compatible entrypoint for the unit2 test runner script. Contributed by Chris Withers.
Tests fixed to be compatible with Python 2.7, where deprecation warnings are silenced by default.
Feature parity with unittest in Python 2.7 RC 1.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-06-08 11:47:16 | |
Categories: Python, Projects Tags: release, unittest2, testing
The Python Testing Tools Taxonomy
The Python Testing Tools Taxonomy is the creation of Grig Gheorghiu and for several year has been an invaluable resource for the Python community.
As with many projects it has become a pain to administer and keep updated, so I'm working with Grig to convert the Trac markup into reStructured Text and maintain it in a bitbucket (mercurial) project that is easier to contribute to.
It's still early days (I'm about one third of the way through) but in the spirit of release early and often, what I've done so far is up online in the new home for the Taxonomy:
If anyone has a prettier template & stylesheet for reStructured Text documents then I would much appreciate it; the default one is a bit 'plain'. If you feel like contributing (which would also be much appreciated), the source is available from the bitbucket taxonomy project.
Like this post? Digg it or Del.icio.us it.
Python 2.7 Beta 2 and unittest2 0.4.1
The second, and hopefully last, beta of Python 2.7 has just been released. The next release will be a release candidate, so it is especially
important that as many of you as possible try the beta and report any problems or incompatibilities!
Interesting new features in Python 2.7 (all of which are also in Python 3.1 or will be in Python 3.2) include:
- DeprecationWarning is now silent by default as it is intended to be a message to developers and not to end-users of code written in Python
- set literal syntax
- dictionary and set comprehensions
- the with statement can now use multiple context managers in one statement
- improved rounding of strings from floating point numbers (a small but very nice change that took a huge amount of work)
- str.format() method supports automatic numbering of the replacement fields (making it less verbose)
- addition of an ordered dictionary to the collections module
- addition of the total_ordering class decorator to the functools module
- addition of the make_archive function to the shutil module
- addition of WeakSet class to the weakref module
- new modules argparse, importlib, ttk and sysconfig
- dictionary based configuration for the logging module
- new type the memoryview
- dictionary views from Python 3 have been provided as the viewkeys, viewvalues and viewitems methods
Because of the Python language moratorium new language features are basically restricted to backports of changes that are already in Python 3. There have been many other library improvements, optimizations and bug fixes.
One important change in Python 2.7, possible the most important, that isn't mentioned in the list above is the set of improvements to the unittest module. There are many new features, including automatic test discovery that allows you to run the tests from a project without having to manually create the test suites and pass them to a test runner. This should make it much easier to get started with testing. The "What's New" document provides a very good summary of these changes, but I have written them up in a bit more detail in the article New Features in unittest.
Most of the changes to unittest were already in Python 2.7 Beta 1, but there have been a few bugfixes since then. I have backported the shiny new unittest to work with Python 2.4, 2.5 & 2.6 as the unittest2 module. To go alongside Python 2.7 Beta 2 there is a new release of unittest2, version 0.4.1, with exactly the same set of features:
You can install unittest2 with: pip install -U unittest2. The best documentation on the new features is the development version of the unittest documentation. Look for "New in Python 2.7" or "Changed in Python 2.7".
Changes in unittest2 0.4.1:
- If test discovery imports a module from the wrong location (usually because the module is globally installed and the user is expecting to run tests against a development version in a different location) then discovery halts with an ImportError and the problem is reported
- Added docstrings to assertRegexpMatches and assertNotRegexpMatches
- Putting functions in test suites no longer crashes
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-05-09 12:51:34 | |
Categories: Python, Projects Tags: release, unittest2, testing
unittest2 0.4.0: Several Major New Features
unittest2 is an enhanced version of unittest including a standard test runner (automatic test discovery), class and module level fixtures (setUpClass / setUpModule etc), many powerful new assert methods, better resource management with addCleanups and a host of other new features. These new features are all going into the Python version of unittest in Python 2.7 and Python 3.2. The point of unittest2 is that it works with Python 2.4 - 2.6; you can use it now.
- unittest2 on PyPI
- Article introducing unittest2
- Development docs for unittest (new features documented)
- Mercurial Development Repository
The latest release is 0.4.0, which roughly equates to the feature set that will be in unittest in Python 2.7 beta 1 which is due out any minute now. The last public release of unittest2 I announced was version 0.2.0 and there have been several new and important features since then.
The most important ones are three new command line options (also available as parameters to unittest2.main()) for running tests
failfast, catch and buffer command line options
The unit2 test runner script now supports three new command line options for both standard test running and test discovery.
-f / --failfast
Stop the test run on the first error or failure.
-c / --catch
Control-c during the test run waits for the current test to end and then reports all the results so far. A second control-c raises the normal KeyboardInterrupt exception.
There are a set of functions implementing this feature available to test framework writers wishing to support this control-c handling. See Signal Handling in the development documentation [1].
-b / --buffer
The standard out and standard error streams are buffered during the test run. Output during a passing test is discarded. Output is echoed normally on test fail or error and is added to the failure messages.
The command line can also be used for test discovery, for running all of the tests in a project or just a subset.
assert method changes
There have been a few new / changed assert methods since unittest2 0.2.0:
assertSameElements has been removed and assertItemsEqual added.
assertItemsEqual asserts that sequences contain the same elements. It is the equivalent of self.assertEqual(sorted(first), sorted(second)) but works with unhashable and unorderable types. It is more useful than the old implementation of assertSameElements that ignored duplicates.
Addition of TestCase.assertNotRegexpMatches.
This is for symmetry with assertRegexpMatches. On failure it shows you how the regular expression matches the text provided.
delta keyword argument for assertAlmostEqual and assertNotAlmostEqual.
I really like this change because the default implementation of assertAlmostEqual is never (almost) useful to me. By default these methods round to a specified number of decimal places. When you use the delta keyword the assertion is that the difference between the two values you provide is less than (or equal to) the delta value. This permits it to be used with non-numeric values:
import datetime delta = datetime.timedelta(seconds=10) second_timestamp = datetime.datetime.now() self.assertAlmostEqual(first_timestamp, second_timestamp, delta=delta)
There are a whole hosts of other minor changes, you can see the full changelog since version 0.2.0 below. The only other feature especially worth mentioning is that test discovery supports the providing of a dotted package name instead of a path name:
unit2 discover -c package.test
CHANGELOG
2010/04/08 - 0.4.0
Addition of removeHandler for removing the control-C handler.
delta keyword argument for assertAlmostEqual and assertNotAlmostEqual.
Addition of -b command line option (and TestResult.buffer) for buffering stdout / stderr during test runs.
Addition of TestCase.assertNotRegexpMatches.
Allow test discovery using dotted module names instead of a path.
All imports requiring the signal module are now optional, for compatiblity with IronPython (or other platforms without this module).
Tests fixed to be compatible with nosetest.
2010/03/26 - 0.3.0
assertSameElements removed and assertItemsEqual added; assert that sequences contain the same elements.
Addition of -f/--failfast command line option, stopping test run on first failure or error.
Addition of -c/--catch command line option for better control-C handling during test runs.
Added BaseTestSuite, for use by frameworks that don't want to support shared class and module fixtures.
Skipped test methods no longer have setUp and tearDown called around them.
Faulty load_tests functions no longer halt test discovery.
Using non-strings for failure messages now works.
Potential for UnicodeDecodeError whilst creating failure messages fixed.
Split out monolithic test module into a package.
BUGFIX: Correct usage message now shown for unit2 scripts.
BUGFIX: __unittest in module globals trims frames from that module in reported stacktraces.
TODO
These are the tasks left to complete before the Python 2.7 release:
- Addition of removeHandler to 2.7 (maybe too late)
- Making removeHandler a context manager?
- Tests for command line handling of -b
- Tests for dotted path discovery
- Docstrings for assertRegexpMatches and assertNotRegexpMatches
- Issues 8301, 8302 and 8303 on the Python bug tracker
- Document BaseTestSuite and _ErrorHolder
- Look at issue 7559.
| [1] | The signal handling, the -c command line option, should work on all CPython platforms. It doesn't work correctly on Jython or IronPython that have missing or incomplete implementations of the signal module. There is a feature in unittest2 that isn't in Python 2.7 (yet), the removeHandler function. This can either be used as a standalone function or as a decorator for test methods that use the signal.SIGINT handler and so can't be executed with the unittest2 control-c handling in place: @unittest2.removeHandler
def test_signal_handling(self):
...
|
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-04-08 23:09:04 | |
Categories: Python, Projects Tags: testing, release, unittest2
WeakSet: A new weak reference collection class
Weak references are a fairly standard language feature that allows you to keep references to objects without preventing them being garbage collected. In Python weak references are provided by the weakref module.
Not all objects are weak referenceable, but instances of user defined classes (plus subclasses of some of the built-in types like dict and list) are. Weak references are callable, and either return the original object or None if it has been garbage collected since the reference was created.
>>> from weakref import ref
>>> class Foo(dict): pass
...
>>> f = Foo()
>>> weak_reference = ref(f)
>>> f is weak_reference()
True
>>> del f
>>> print weak_reference()
None
Weak references are useful for tracking objects without leaking memory by keeping objects alive beyond their natural lifetime. The Ordered Dictionary (new in Python 2.7 and 3.1) very cleverly uses a doubly-linked list to maintain order but still provide efficient member deletion (O(1) rather than O(n)). The list is linked with weak references to avoid creating a cycle, which could then only be collected by the cyclic garbage collector rather than the standard Python deterministic reference counting. Raymond explains the ordered dictionary implementation in this excellent PyCon Presentation.
The weakref module includes several container classes like the WeakKeyDictionary and WeakValueDictionary for managing collections of weak references. These collections abstract away the weak references themselves so you only work with the container classes and your objects. As objects go away they are automatically removed from the collection.
New in Python 3.1 is the WeakSet which was actually implemented to manage class registration in the Abstract Base Class machinery. I've now backported this to Python 2.7, as it is a generally useful class, and put it up on PyPI for Python 2.6 and 2.5. Many thanks to Thomas Wouters for the original implementation and the very good set of tests that made backporting it trivially easy.
You can install it with: pip install weakrefset
Usage is straightforward. You don't need to deal with weak references themselves, the WeakSet manages the references and you can add / remove / iterate over your objects directly:
>>> from weakrefset import WeakSet
>>> class Foo(object): pass
...
>>> f1 = Foo()
>>> f2 = Foo()
>>> refs = WeakSet([f1, f2])
>>> f3 = Foo()
>>> refs.add(f3)
>>> refs.remove(f1)
>>> del f2
>>> f3 in refs
True
>>> for f in refs:
... print f
...
<__main__.Foo object at 0x40e410>
To be compatible with Python 2.5, 2.6 and future versions of Python you can import it like this:
try:
# location in Python 2.7 and 3.1
from weakref import WeakSet
except ImportError:
# separately installed
from weakrefset import WeakSet
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2010-03-30 15:40:27 | |
Categories: Python, Projects Tags: weak references, release, module
Archives
Counter...

