0

I have a thread that loops receiving socket data and printing it:

def post(self):
    while True:
        try:
            data = pickle.loads(self.sock.recv(1024))
            print data[0] % tuple(data[1])
        except (socket.error, EOFError): 
            break

I then have a GUI that redirects stdout to a textctrl like so:

import wx
import sys
import threading

class Redirect: 
    def __init__(self, ctrl):
        self.out = ctrl
    def write(self, string): 
        wx.CallAfter(self.out.AppendText,string)

class GUI(wx.Frame):
    def __init__(self, parent):
        self.monitor = wx.TextCtrl(self, wx.ID_ANY, \
                               style = wx.TE_MULTILINE | wx.TE_READONLY)
        redir = Redirect(self.monitor)
        sys.stdout = redir

        self.sizer = BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.monitor, 1, wx.GROW | wx.ALL)
        self.SetSizer(self.sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App(False)
    frame = GUI(None)
    app.MainLoop()

I've also got a second textctrl that accepts input (left out for simplicity). The issue is that sometimes CallAfter(AppendText,string) is not printing out the entire string. It is very rare, but sometimes the print will just be stopped abruptly in the middle of the string, at which point the next string is printed (and the app continues printing the strings as they're received as if nothing happened).

I've no idea what's causing this behavior, I've tried to induce it by typing into the second textctrl to see if that's causing it, but even if I do nothing, these "partial prints" appear every now and then. What's going on?

Shaunak Amin
  • 181
  • 5
  • 12
  • I should add that if I use "print data" the GUI appends \n after the "partial print," but if I use sys.stdout.write(data+'\n') it does not append the new line. Is the data somehow being corrupted between my print call and CallAfter's AppendText call? It also seems strange that Fenikso showed it consistently happens on line 30 and 63. – Shaunak Amin Apr 06 '11 at 21:45
  • For now I'm assuming the issue is that the data is from another thread and is somehow being corrupted. Not sure how AppendText works, but maybe during the print, the post thread sets data = to the next sock.recv() – Shaunak Amin Apr 06 '11 at 23:13

1 Answers1

1

This is not a real answer, just a way how to replicate the problem:

import wx
import sys
import threading

def post():
    a = 0
    for stop in range(100):
        a = (a + 1) % 10
        data = str(a) * 1000 + " <END>" 
        print(data)
    for i, line in enumerate(frame.monitor.Value.split("\n")[:-1]):
        if not line.endswith(" <END>"):
            print("Invalid line %d" % (i+1))


class Redirect: 
    def __init__(self, ctrl):
        self.out = ctrl

    def write(self, string): 
        wx.CallAfter(self.out.AppendText, string)


class GUI(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.monitor = wx.TextCtrl(self, wx.ID_ANY, style = wx.TE_MULTILINE | wx.TE_READONLY)
        redir = Redirect(self.monitor)
        sys.stdout = redir

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.monitor, 1, wx.GROW | wx.ALL)
        self.SetSizer(self.sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App(False)
    frame = GUI(None)
    t = threading.Thread(target=post)
    t.start()
    app.MainLoop()

When ran on my setup, I get (usually, not always):

Invalid line 30
Invalid line 63

I tried playing with delays and mutexes / locks, but it seem that I did not find why this happens yet.

Fenikso
  • 9,251
  • 5
  • 44
  • 72
  • Thanks for the runnable sample. I wasn't sure if maybe the socket was just receiving incomplete data, but I guess you've shown that's not the case. – Shaunak Amin Apr 06 '11 at 17:42
  • @Shaunak Amin - yes, always the same. Or they are not anywhere in about 10% of runs. Really weird. – Fenikso Apr 06 '11 at 19:12
  • I added an li.append(data) to the post thread and posted the data for lines 30 and 63: data for each line does end with " " so the issue is with CallAfter or AppendText not printing the string out in its entirety... I just don't know why it would continue printing afterwards without raising an error or printing a trace, seems like it should if it somehow aborts printing the string. – Shaunak Amin Apr 06 '11 at 21:27
  • when I call post() without threading, the issue does not occur, so I think the thread sets data = sock.recv() before CallAfter(AppendText,string) has finished. Usually the print is faster than sock.recv(), but occasionally not. Odd that it always happens on the same lines though. – Shaunak Amin Apr 06 '11 at 23:16
  • I'll just give you the checkmark since you took the time to investigate, thanks for the help. – Shaunak Amin Apr 06 '11 at 23:20
  • Hmmm. It still gives weird results on my system even without sock.recv() and even using locks or 1 second delays did not help. I am going to ask in wx mailing list, because I am kind of interested in the issue. You can also try using subclasses of wx.Log class. – Fenikso Apr 07 '11 at 04:16
  • sorry I didn't mean sock.recv, I meant in your example, if you simply call post() instead of threading.Thread(target = post), the error goes away. – Shaunak Amin Apr 07 '11 at 18:43