Python Programming, news on the Voidspace Python Projects and all things techie.

  • Front page
  • All By Date
  • All By Category
  • RSS Feed
#1235

Tests that fail one day every four years

spacer Some code looks harmless but has hidden bugs lurking in its nether regions. Code that handles dates is notorious for this, and this being February 29th (the coders' halloween) it's time for the bugs to come crawling out of the woodwork.

Some of our tests were constructing expected dates, one year from today, with the following code:

from datetime import datetime

now = datetime.utcnow()
then = datetime(now.year + 1, now.month, now.day)

Of course if you run this code today, then it tries to construct a datetime for February 29th 2013, which fails because that date doesn't exist.

When I posted this on twitter a few people suggested that instead we should have used timedelta(days=365) instead. Again, this works most of the time - but if you want a date exactly one year from now it will fail in leap years when used before February 29th:

>>> from datetime import datetime, timedelta
>>> datetime(2012, 2, 27) + timedelta(days=365)
datetime.datetime(2013, 2, 26, 0, 0)

The correct fix is to use the wonderful dateutil module, in particular the dateutil.relativedelta.relativedelta:

>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> datetime.utcnow() + relativedelta(years=1)
datetime.datetime(2013, 2, 28, 15, 20, 21, 546755)
>>> datetime(2012, 2, 27) + relativedelta(years=1)
datetime.datetime(2013, 2, 27, 0, 0)

And as another hint, always use datetime.utcnow() instead of datetime.now() to avoid horrible timezone nightmares (exactly which timezone are your servers in?).

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-02-29 15:24:39 | |

Categories: Python, Hacking Tags: bugs, dates


#1234

mock 0.8 released

spacer After more than six months development work mock 0.8 has been released. 0.8 is a big release with many new features, general improvements and bugfixes.

You can download mock 0.8.0 final from the PyPI page or install it with:

pip install -U mock

mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects.

The only changes in mock 0.8.0 final since 0.8rc2 are:

  • Improved repr of sentinel objects
  • ANY can be used for comparisons against call objects
  • The return value of MagicMock.__iter__ can be set to any iterable and isn't required to be an iterator

The full changelog since 0.7.2 is pretty large. Hopefully I'll do a series of blog posts explaining the major new features and how to use them. Several common patterns of testing with mock have become simpler in 0.8.

Several of the changes, particularly the improved reprs and error messages, come for free without you having to know anything about the other new features. For documentation on the new features, browse the links in the changelog.

Here's a brief example of the improved reprs / failure messages you get with mock 0.8:

>>> from mock import MagicMock
>>> m = MagicMock(name='foo')
>>> m.method(1, 2, 3).attribute['foo']
<MagicMock name='foo.method().attribute.__getitem__()' id='4300665616'>
>>> m.method.call_args
call(1, 2, 3)
>>> m.method.assert_called_with('some args')
Traceback (most recent call last):
  ...
AssertionError: Expected call: method('some args')
Actual call: method(1, 2, 3)

One of the best new features, for making assertions about several calls at once, is mock_calls in conjunction with the call object:

>>> from mock import MagicMock, call
>>> m = MagicMock(name='foo')
>>> config = {'method.return_value.chained.return_value.nested.return_value': 3}
>>> m.configure_mock(**config)
>>> m.method('arg').chained().nested('call')
3
>>> m.mock_calls
[call.method('arg'),
 call.method().chained(),
 call.method().chained().nested('call')]
>>> m.mock_calls == [call.method('arg'),
...  call.method().chained(),
...  call.method().chained().nested('call')]
True
>>> expected_calls = call.method('arg').chained().nested('call').call_list()
>>> m.mock_calls == expected_calls
True

If you're still using mock 0.7, and can't upgrade all your test code yet, the 0.7 documentation is available online here.

The full List of changes since 0.7:

mock 0.8.0 is the last version that will support Python 2.4.

  • Addition of mock_calls list for all calls (including magic methods and chained calls)
  • patch and patch.object now create a MagicMock instead of a Mock by default
  • The patchers (patch, patch.object and patch.dict), plus Mock and MagicMock, take arbitrary keyword arguments for configuration
  • New mock method configure_mock for setting attributes and return values / side effects on the mock and its attributes
  • New mock assert methods assert_any_call and assert_has_calls
  • Implemented auto-speccing (recursive, lazy speccing of mocks with mocked signatures for functions/methods), as the autospec argument to patch
  • Added the create_autospec function for manually creating 'auto-specced' mocks
  • patch.multiple for doing multiple patches in a single call, using keyword arguments
  • Setting side_effect to an iterable will cause calls to the mock to return the next value from the iterable
  • New new_callable argument to patch and patch.object allowing you to pass in a class or callable object (instead of MagicMock) that will be called to replace the object being patched
  • Addition of NonCallableMock and NonCallableMagicMock, mocks without a __call__ method
  • Addition of mock_add_spec method for adding (or changing) a spec on an existing mock
  • Protocol methods on MagicMock are magic mocks, and are created lazily on first lookup. This means the result of calling a protocol method is a MagicMock instead of a Mock as it was previously
  • Addition of attach_mock method
  • Added ANY for ignoring arguments in assert_called_with calls
  • Addition of call helper object
  • Improved repr for mocks
  • Improved repr for call_args and entries in call_args_list, method_calls and mock_calls
  • Improved repr for sentinel objects
  • patch lookup is done at use time not at decoration time
  • In Python 2.6 or more recent, dir on a mock will report all the dynamically created attributes (or the full list of attributes if there is a spec) as well as all the mock methods and attributes.
  • Module level FILTER_DIR added to control whether dir(mock) filters private attributes. True by default.
  • patch.TEST_PREFIX for controlling how patchers recognise test methods when used to decorate a class
  • Support for using Java exceptions as a side_effect on Jython
  • Mock call lists (call_args_list, method_calls & mock_calls) are now custom list objects that allow membership tests for "sub lists" and have a nicer representation if you str or print them
  • Mocks attached as attributes or return values to other mocks have calls recorded in method_calls and mock_calls of the parent (unless a name is already set on the child)
  • Improved failure messages for assert_called_with and assert_called_once_with
  • The return value of the MagicMock.__iter__ method can be set to any iterable and isn't required to be an iterator
  • Added the Mock API (assert_called_with etc) to functions created by mocksignature
  • Tuples as well as lists can be used to specify allowed methods for spec & spec_set arguments
  • Calling stop on an unstarted patcher fails with a more meaningful error message
  • Renamed the internal classes Sentinel and SentinelObject to prevent abuse
  • BUGFIX: an error creating a patch, with nested patch decorators, won't leave patches in place
  • BUGFIX: __truediv__ and __rtruediv__ not available as magic methods on mocks in Python 3
  • BUGFIX: assert_called_with / assert_called_once_with can be used with self as a keyword argument
  • BUGFIX: when patching a class with an explicit spec / spec_set (not a boolean) it applies "spec inheritance" to the return value of the created mock (the "instance")
  • BUGFIX: remove the __unittest marker causing traceback truncation
  • Removal of deprecated patch_object
  • Private attributes _name, _methods, '_children', _wraps and _parent (etc) renamed to reduce likelihood of clash with user attributes.
  • Added license file to the distribution

Thanks to all those who suggested features, provided patches and helped test the mock 0.8 release. There are still plenty of ways mock can continue to improve, you can browse the issues list to get an idea of some of the outstanding feature requests and suggestions.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-02-16 12:56:14 | |

Categories: Projects, Python Tags: mock, release, testing


#1233

Callable object with state using generators

spacer It's often convenient to create callable objects that maintain some kind of state. In Python we can do this with objects that implement the __call__ method and store the state as instance attributes. Here's the canonical example with a counter:

>>> class Counter(object):
...     def __init__(self, start):
...         self.value = start
...     def __call__(self):
...         value = self.value
...         self.value += 1
...         return value
...
>>> counter = Counter(0)
>>> counter()
0
>>> counter()
1
>>> counter()
2

Generators can be seen as objects that implement the iteration protocol, but maintain state within the generator function instead of as instance attributes. This makes them much simpler to write, and much easier to read, than manually implementing the iteration protocol.

This example iterator never terminates, so we obtain values by manually calling next:

>>> class Iter(object):
...     def __init__(self, start):
...         self.value = start
...     def __iter__(self):
...         return self
...     def next(self):
...         value = self.value
...         self.value += 1
...         return value
...
>>> counter = Iter(0)
>>> next(counter)
0
>>> next(counter)
1
>>> next(counter)
2

The same iterator implemented as a generator is much simpler and the state is stored as local variables in the generator:

>>> def generator(start):
...     value = start
...     while True:
...         yield value
...         value += 1
...
>>> gen = generator(0)
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2

In recent versions of Python generators were enhanced with a send method to enable them to act like coroutines.

>>> def echo():
...     result = None
...     while True:
...         result = (yield result)
...
>>> f = echo()
>>> next(f)  # initialise generator
>>> f.send('fish')
'fish'
>>> f.send('eggs')
'eggs'
>>> f.send('ham')
'ham'

(Note that we can't send to an unstarted generator - hence the first call to next to initialise the generator.)

We can use the send method as a way of providing a callable object with state. I first saw this trick in this recipe for a highly optimized lru cache by Raymond Hettinger. The callable object is the send method itself, and as with any generator the state is stored as local variables.

Here's our counter as a generator:

>>> def counter(start):
...     yield None
...     value = start
...     while True:
...         ignored = yield value
...         value += 1
...
>>> gen = counter(0)
>>> next(gen)
>>> f = gen.send
>>> f(None)
0
>>> f(None)
1
>>> f(None)
2
>>> f(None)
3

Some observations. Firstly send takes one argument and one argument only. In this example we're ignoring the value sent into the generator, but send must be called with one argument and can't be called with more. So it's mostly useful for callable objects that take a single argument...

Secondly, this performs very well. Function calls are expensive (relatively) in Python because each invocation creates a new frame object (or reuses a zombie frame from the pool - but I digress) for storing the local variables etc. Generators are implemented with a "trick" that keeps the frame object alive, so that the next step of the generator can simply continue execution after the last yield. So our callable object implemented as a generator doesn't have the overhead of a normal function call...

The main advantage this approach has is that it's more readable than the version with __call__. To make it more pleasant to use, we can wrap creating our counter in a convenience function:

>>> def get_counter(start):
...     c = counter(start)
...     next(c)
...     return c.send
...
>>> c = get_counter(0)
>>> c(None)
0
>>> c(None)
1
>>> c(None)
2

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-01-22 15:05:46 | |

Categories: Python, Hacking Tags: generators, recipe


#1232

Simple mocking of open as a context manager

spacer Using open as a context manager is a great way to ensure your file handles are closed properly and is becoming common:

with open('/some/path', 'w') as f:
    f.write('something')

The issue is that even if you mock out the call to open it is the returned object that is used as a context manager (and has __enter__ and __exit__ called).

Using MagicMock from the mock library, we can mock out context managers very simply. However, mocking open is fiddly enough that a helper function is useful. Here mock_open creates and configures a MagicMock that behaves as a file context manager.

from mock import inPy3k, MagicMock

if inPy3k:
    file_spec = ['_CHUNK_SIZE', '__enter__', '__eq__', '__exit__',
        '__format__', '__ge__', '__gt__', '__hash__', '__iter__', '__le__',
        '__lt__', '__ne__', '__next__', '__repr__', '__str__',
        '_checkClosed', '_checkReadable', '_checkSeekable',
        '_checkWritable', 'buffer', 'close', 'closed', 'detach',
        'encoding', 'errors', 'fileno', 'flush', 'isatty',
        'line_buffering', 'mode', 'name',
        'newlines', 'peek', 'raw', 'read', 'read1', 'readable',
        'readinto', 'readline', 'readlines', 'seek', 'seekable', 'tell',
        'truncate', 'writable', 'write', 'writelines']
else:
    file_spec = file

def mock_open(mock=None, data=None):
    if mock is None:
        mock = MagicMock(spec=file_spec)

    handle = MagicMock(spec=file_spec)
    handle.write.return_value = None
    if data is None:
        handle.__enter__.return_value = handle
    else:
        handle.__enter__.return_value = data
    mock.return_value = handle
    return mock
>>> m = mock_open()
>>> with patch('__main__.open', m, create=True):
...     with open('foo', 'w') as h:
...         h.write('some stuff')
...
>>> m.assert_called_once_with('foo', 'w')
>>> m.mock_calls
[call('foo', 'w'),
 call().__enter__(),
 call().write('some stuff'),
 call().__exit__(None, None, None)]
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

And for reading files, using a StringIO to represent the file handle:

>>> from StringIO import StringIO
>>> m = mock_open(data=StringIO('foo bar baz'))
>>> with patch('__main__.open', m, create=True):
...     with open('foo') as h:
...         result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'foo bar baz'

Note that the StringIO will only be used for the data if open is used as a context manager. If you just configure and use mocks they will work whichever way open is used.

This helper function will be built into mock 0.9.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-01-13 12:18:35 |