Hi,

> On 12/02/2010 03:43 PM, Dan Fairs wrote:
>> My gut feeling is that this boils down to this vastly simplified 
>> demonstration of how list() works:
>> 
>>>>> class Foo(object):
>> ...   def __len__(self):
>> ...     print 'len called'
>> ...     raise ValueError
>> ...   def __iter__(self):
>> ...     return iter([1,2,3])
>> ...
>>>>> a = Foo()
>>>>> list(a)
>> len called
>> [1, 2, 3]
>> 
>> Here, you can see that when converting to a list, Python calls __len__, and 
>> if that raises an exception, discards it and goes on to call __iter__.
>> 
>> So - my hypothesis (unproved, as I could benefit from someone with deeper 
>> ORM knowledge) is that the call to list() in my original test case calls 
>> QuerySet.__len__(), which ends up raising a DatabaseError (caused by an 
>> underlying database lock, the behaviour I'm actually testing for). Python's 
>> subsequent call to QuerySet.__iter__() succeeds, but ends up returning an 
>> empty iterator due to some pre-existing state *handwaving here*.
>> 
>> It's the handwaving bit I'm not sure about :). Does that hypothesis sound 
>> plausible? It seems to be borne out by the snippet below, where I've removed 
>> the underlying table:
> 
> I ran into this issue, too. My workaround was to place a
> 
>    if hasattr(self._iter, '__len__'):
>        len(self._iter)
> 
> and the same for self.generator in QuerySet.__len__.
> 
> Btw, there's a bug report for this from 2009: 
> http://bugs.python.org/issue1242657
> 

Right - I remember that regression actually.

> I was wondering whether the bug still exists in Python 2.x because I take it 
> only AttributeError and TypeError should be ignored while calling __len__ in 
> list() -- if that's the case, the bug is definitely still present.

True - I'll give it a try in 2.7 when I have a moment to grab and build it. 
That doesn't help us that much though, as 2.6 (which I'm running on, 2.6.1 to 
be precise) is a supported version.

> 
> Anyway, I propose to call len() on those iterators, if possible, before 
> calling list() because otherwise all bugs in backends will be swallowed.

That makes sense. I'd considered proposing storing any exception raised in 
__len__ on an instance variable to be re-raised later; but your solution is 
better, as you'd get better tracebacks.

Should I raise a bug for this behaviour? Working up a test and patch should be 
straightforward for this one, if we agree it's a bug that needs fixing (even if 
it's a workaround for the underlying Python issue).

Cheers,
Dan
--
Dan Fairs | [email protected] | www.fezconsulting.com


-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to