Doctest's output different from shell's output

Go To StackoverFlow.com

0

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.

2012-04-03 23:16
by Nelson Gregório
You should probably narrow down your code to the shortest version still generating the same behaviour. That way you probably find the problem yourself - marue 2012-04-03 23:34
You should really have a look to this: http://stackoverflow.com/questions/how-to-ask Allthough you provide a lot of code you leave us in the dark about the circumstances under which you use it: at least show your code for the tests - marue 2012-04-03 23:46
Question rewritten - Nelson Gregório 2012-04-04 11:25
Great! Now it's possible to understand your problem. Which doesn't necessarily mean that i'm able to solve it ;) Let me see.. - marue 2012-04-04 15:48


2

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.

2012-04-04 16:04
by marue
How can I "clean" the parameters without losing the posts in it ? I tried a few things inside the search function but I don't really now how - Nelson Gregório 2012-04-04 16:57
Oops, sorry. Corrected my answer, the explanation was wrong. Maybe i have time to look into it again later - marue 2012-04-04 17:43
The explanation was actually wrong, like you said. I did some extra tests and the text part is not working properl - Nelson Gregório 2012-04-04 20:00
And now I'm not so sure. Mixed results with each test. I'm getting pretty confused - Nelson Gregório 2012-04-04 20:38
Found your problem. See my answer. Back to the Python schoolbench, Nelson ; - marue 2012-04-04 21:50
I think I understood the problem. Your example helped alot. I actually didn't know that changed "b" would also change "a". Thanks : - Nelson Gregório 2012-04-04 22:14
You're welcome - marue 2012-04-04 22:17