Using the same code in a doctest and in the shell yields different outputs.
I have a function called a() that runs through a few tests.
Those same tests are used in a doctest (test() ).
With a() I get OBJECT-BLANKLINE-OBJECT while the test() gives me an error and shows only the first OBJECT.
Is this a flaw in the doctest module?
Here's the whole file with both a() and test() in the top:
'''
>>> u1 = User("luis@fc.up.pt", "simples")
>>> u2 = User("ana@fc.up.pt", "complicada")
>>> u2.askFriend(u1)
>>> u1.recvRequest(u2)
>>> u1.confirmFriend(u2)
>>> p1 = Post(u1, "O ultimo post", "http://www.wikipedia.org")
>>> p2 = Post(u2, "A ultima resposta", "http://www.google.com")
>>> c = Comments()
>>> c.add(p1)
>>> c.add(p2)
>>> f1 = c.search(user="ana@fc.up.pt")
>>> print(f1)
A ultima resposta
http://www.google.com
0 Gosto, 0 Nao gosto
>>> f2 = c.search(likes=1)
>>> print(f2)
<BLANKLINE>
>>> f3 = c.search(text='post')
>>> print(f3)
O ultimo post
http://www.wikipedia.org
0 Gosto, 0 Nao gosto
<BLANKLINE>
A ultima resposta
http://www.google.com
0 Gosto, 0 Nao gosto
'''
def test():
import doctest
doctest.testmod()
def a():
u1 = User("luis@fc.up.pt", "simples")
u2 = User("ana@fc.up.pt", "complicada")
u2.askFriend(u1)
u1.recvRequest(u2)
u1.confirmFriend(u2)
p1 = Post(u1, "O ultimo post", "http://www.wikipedia.org")
p2 = Post(u2, "A ultima resposta", "http://www.google.com")
c = Comments()
c.add(p1)
c.add(p2)
f3 = c.search(text="post")
print(f3)
import string
class User:
def __init__(self,email,passwd):
self.email = email
self.passwd = passwd
self.name = None
self.year = None
self.active = True
self.recv = []
self.conf = []
self.setPassword(self.passwd)
def __str__(self):
if self.name == None and self.active == True:
return str(self.email) + ':' + 'ativa'
elif self.name != None and self.active == True:
return str(self.name) + ':' + str(self.email) + ':' + 'ativa'
elif self.name != None and self.active == False:
return str(self.name) + ':' + str(self.email) + ':' + 'inativa'
else:
return str(self.email) + ':' + 'inativa'
def getEmail(self):
return self.email
def setPassword(self,passwd):
abc = string.ascii_lowercase
cifra = abc[3:] + abc[:3]
dec = list(passwd)
passwdCif = ""
for i in dec:
cif = cifra[abc.find(i)]
passwdCif += cif
self.passwd = passwdCif
def getPassword(self):
return self.passwd
def setName(self,name):
self.name = name
def getName(self):
return self.name
def setBirth(self,year,month,day):
self.year = year
self.month = month
self.day = day
def getBirth(self):
if self.year == None:
return None
else:
return '(' + str(self.year) + ', ' + str(self.month) + ', ' + str(self.day) + ')'
def isActive(self):
if self.active == True:
return True
else:
return False
def setActive(self):
self.active = True
def setInactive(self):
self.active = False
def askFriend(self,u):
self.conf.append(u)
def recvRequest(self,u):
self.recv.append(u)
def confirmFriend(self,u):
if len(self.recv) == 0:
return None
else:
for i in self.recv:
if i == u:
self.recv.remove(i)
self.conf.append(i)
else:
return None
def isFriend(self,u):
if u in self.conf:
return True
elif self == u:
return True
else:
return False
def showPending(self):
if len(self.recv) == 0:
return None
else:
for i in self.recv:
print i
def showFriends(self):
if len(self.conf) == 0:
return None
else:
for i in self.conf:
print i
class Post():
def __init__(self,u,text='',link=None):
self.u = u
self.text = text
self.link = link
self.seg = None
self.ant = None
self.likeList = []
self.dislikeList = []
def __str__(self):
if self.link == None:
return str(self.text) + '\n' + str(len(self.likeList)) + ' Gosto, ' + str(len(self.dislikeList)) + ' Nao gosto'
else:
return str(self.text) + '\n' + str(self.link) + '\n' + str(len(self.likeList)) + ' Gosto, ' + str(len(self.dislikeList)) + ' Nao gosto'
def updateText(self,text):
self.text = text
def updateLink(self,link):
self.link = link
def like(self,u):
if (u in self.u.conf) or (u == self.u):
if u in self.dislikeList:
self.dislikeList.remove(u)
self.likeList.append(u)
elif u in self.likeList:
return None
else:
self.likeList.append(u)
else:
return None
def dislike(self,u):
if (u in self.u.conf) or (u == self.u):
if u in self.likeList:
self.likeList.remove(u)
self.dislikeList.append(u)
elif u in self.dislikeList:
return None
else:
self.dislikeList.append(u)
else:
return None
class Comments():
def __init__(self, u=None, text='', link=None):
self.u = u
self.text = text
self.link = link
self.topo = None
self.fim = None
def __str__(self):
actual = self.topo
s = ''
if actual == None:
return ''
while actual != None:
if actual.seg == None:
s += str(actual)
actual = actual.seg
elif actual.seg != None:
s += str(actual) + '\n' + '\n'
actual = actual.seg
return s
def add(self,comment):
if self.topo == None:
comment.ant = None
comment.seg = None
self.topo = comment
self.fim = comment
else:
comment.ant = None
comment.seg = self.topo
self.topo.ant = comment
self.topo = comment
def remove(self,comment):
actual = self.topo
if (self.topo == self.fim) and (self.topo == comment):
self.topo = None
self.fim = None
actual = None
while actual!=None:
if actual == comment:
if actual.ant == None:
self.topo = actual.seg
actual.seg.ant = None
elif actual.seg == None:
self.fim = actual.ant
actual.ant.seg = None
else:
actual.seg.ant = actual.ant
actual.ant.seg = actual.seg
break
else:
actual = actual.seg
def countLike(self):
count = 0
actual = self.topo
while actual != None:
if len(actual.likeList) >= 1:
count += 1
actual = actual.seg
else:
actual = actual.seg
return count
def showRecentComments(self,n):
count = 1
actual = self.topo
sC = ''
if actual == None:
return None
while actual != None:
if count < n:
if actual.seg == None:
sC += str(actual)
count += 1
actual = actual.seg
else:
sC += str(actual) + '\n' + '\n'
count += 1
actual = actual.seg
elif count == n:
sC += str(actual)
count += 1
actual = actual.seg
elif count > n:
break
print sC
def search(self, user=None, likes=None, dislikes=None, text=None):
result = []
actual = self.topo
cR = Comments()
if actual == None:
return None
while actual != None:
if user != None:
if actual.u.email != user:
actual = actual.seg
elif actual.u.email == user:
result.append(actual)
actual = actual.seg
elif user == None:
break
actual = self.topo
while actual != None:
if likes != None:
if likes > len(actual.likeList):
actual = actual.seg
elif likes <= len(actual.likeList):
if actual in result:
actual = actual.seg
else:
result.append(actual)
actual = actual.seg
elif likes == None:
break
actual = self.topo
while actual != None:
if dislikes != None:
if dislikes > len(actual.dislikeList):
actual = actual.seg
elif dislikes <= len(actual.dislikeList):
if actual in result:
actual = actual.seg
else:
result.append(actual)
actual = actual.seg
elif dislikes == None:
break
actual = self.topo
while actual != None:
if text != None:
if text not in actual.text:
actual = actual.seg
elif text in actual.text:
if actual in result:
actual = actual.seg
else:
result.append(actual)
actual = actual.seg
elif text == None:
break
if len(result) != 0:
for i in result:
cR.add(i)
return cR
And here's the output I get using both a() and test():
>>> a()
O ultimo post
http://www.wikipedia.org
0 Gosto, 0 Nao gosto
A ultima resposta
http://www.google.com
0 Gosto, 0 Nao gosto
>>> test()
**********************************************************************
File "__main__", line 22, in __main__
Failed example:
print(f3)
Expected:
O ultimo post
http://www.wikipedia.org
0 Gosto, 0 Nao gosto
<BLANKLINE>
A ultima resposta
http://www.google.com
0 Gosto, 0 Nao gosto
Got:
A ultima resposta
http://www.google.com
0 Gosto, 0 Nao gosto
**********************************************************************
1 items had failures:
1 of 16 in __main__
***Test Failed*** 1 failures.
>>>
I'd also like to apologize for my previous badly asked question.
Hopefully this one will help me and others.
There is one obvious difference between your doctest and your function: While your function a does this:
# previous code
c.add(p2)
f3 = c.search(text="post")
print(f3)
your doctest does this:
# previous code
c.add(p2)
f1 = c.search(user="ana@fc.up.pt")
print(f1)
f2 = c.search(likes=1)
print(f2)
f3 = c.search(text='post')
print(f3)
I do not know what exactly happens there, but probably you should write your function to do the same thing. If the result still is different, there is something wrong with doctest, otherwise with your code.
edit:
Now i start to understand why you are trouble, and it is deeeeeeep trouble. It starts with this line right on top of your search function:
actual = self.topo
I think you are not aware of the way Python assigns properties. Later in your code you change actual, and then assign self.topo to actual again:
actual.seg = "somevalue" # sorry, i can't remember what you did"
actual = self.topo
That second line is completely meaningless, as actual already is self.topo! It is not the value of self.topo but it is self.topo.
Instead of writing
actual.seg = "somevalue"
you could just write
self.topo.seg = "somevalue"
Both lines do exactly the same. So while you thought you were altering some independent property actual
you really changed self.topo all the time. And that means that actual
is not equal at each start of the function, but always has the value it had at the last run of c.search
.
I have just seen that you are doing this in some of the other functions as well, which means you are altering the state of your Comments instance c every time you call such a function. That, of course, results in different output for each test.
To make this more understandable, here is an example with a list:
>>> a_list = ['one','two','three']
>>> b_list = a_list
>>> b_list.pop()
'three'
>>> a_list
['one', 'two']
As you see: Allthoug i did pop the element from b_list, it is gone from a_list as well. This is because the statement b_list = a_list
means literally that b_list is now the same as a_list.
Hope this helps.