The Perils of PyContract (and Generators)

Nick Daly nick.m.daly at gmail.com
Wed Aug 5 14:56:14 CEST 2009


On Wed, 5 Aug 2009 03:06 pm Nick Daly wrote:

> The problem actually lies in the contract.  Generally, the PyContract
> shouldn't affect the return values or in any way modify the code, which
> it doesn't, as long as the function returns a list values (the way the
> code had in fact originally been written).  However, the contract
> mentioned above is actually quite wrong for a generator.

Yes, because you are conflating the items yielded from the
generator with the generator object returned from the generator
function "find_files". You can't look inside the generator object
without using up whichever items you look at.

[...]
> Correcting the above example involves doing nothing more than
> simplifying the contract:
>
>     post:
>         name in __return__

That can't be right, not unless PyContract is doing something I don't
expect. I expect that would be equivalent of:

'fish' in <generator object>

which should fail:

>>> __return__ = find_files('fish')  # a generator object
>>> 'fish' in __return__
False
>>>
>>> __return__ = find_files('fish')
>>> __return__ = list(__return__)
>>> 'fish' in __return__
False
>>> __return__
['one fish', 'two fish', 'red fish', 'blue fish']

Of course, I may be mistaking what PyContract is doing.

-----

No, you're absolutely right, I realized the error in my email moments
after sending it, I was just hoping no one was paying enough attention
to notice.  I'm essentially asking PyContract to operate outside of the
generator (it needs to check the return value of a generator, without
changing its internal state), which doesn't seem to have an easy
solution unless generators have some fantastic reverse_states function
that I haven't heard about.  I suppose one could be built, saving off
the values of all local variables for later restoration, but that would
still be dependent on the state of the rest of the machine and could not
guarantee giving the same answer twice in a row.

So, the moral of the story seems to be that PyContract's post-return
value checking doesn't mix with generators at all.  Sort of sad, because
they're two neat flavors that would go great together but are instead
technically incompatible.  A decent set of unit-tests would accomplish
the same thing much less destructively anyway.

Nick



More information about the Python-list mailing list