PyPy Regression with Python on Iterable expansions
Created originally on Bitbucket by pedrorodriguez (Pedro Rodriguez)
I am the main developer on github.com/EntilZha/ScalaFunctional and in working on it I found a regression with Python. I don't know if it is an intended regression, but it caused some unexpected behavior which I was able to narrow down. If you want the full explanation, it is on the issue on the project here: https://github.com/EntilZha/ScalaFunctional/issues/25
Below is a minimal code example showing the regression between pypy and python. The primary problem is how pypy
is doing expansions of iterables (list()
, set()
, dict()
, ...). When those are expanded pypy
will call __iter__
, then __len__
. Python will only call __iter__
.
#!python
from collections import Iterable
class A(object):
def __init__(self, seq):
self.l = seq
def __getitem__(self, item):
print "DEBUG:getitem called"
return self.l[item]
def __iter__(self):
print "DEBUG:iter called"
return iter(self.l)
def __len__(self):
print "DEBUG:len called"
if isinstance(self.l, Iterable):
self.l = list(self.l)
return len(self.l)
class B(object):
def __init__(self, seq):
self.l = seq
def __iter__(self):
print "DEBUG:iter called"
return iter(self.l)
# set interchangable with list/dict
print "Calling set(A([1, 2]))"
a = A([1, 2])
print set(a)
print "Calling set(B([1, 2]))"
b = B([1, 2])
print set(b)
print "Calling union"
s = set([1, 2, 3]).union([4, 5])
c = A(iter(s))
print set(c)
#!python
$ python iterable.py
Calling set(A([1, 2]))
DEBUG:iter called
set([1, 2])
Calling set(B([1, 2]))
DEBUG:iter called
set([1, 2])
Calling union
DEBUG:iter called
set([1, 2, 3, 4, 5])
$ pypy iterable.py
Calling set(A([1, 2]))
DEBUG:len called
DEBUG:iter called
[1, 2]
Calling set(B([1, 2]))
DEBUG:iter called
set([1, 2])
Calling union
DEBUG:iter called
DEBUG:len called
set([])
Since I am unfamiliar with pypy
I don't know if the regression is intended or unintended.