Skip to content

Commit d53407a

Browse files
committed
PythonInterpreter: now runs with XmlRpc over pipe (required a few important bug fixes, but still potentially fragile if a spawned python process or C extension generates stdout/stderr on the pipe). Will add option to switch back to a regular IP connection
1 parent 60b616a commit d53407a

2 files changed

Lines changed: 35 additions & 39 deletions

File tree

‎PythonInterpreter/PythonInterpCtrl.cpp‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ long PythonInterpCtrl::LaunchProcess(const wxString &processcmd, const wxArraySt
211211
{
212212
if(!IsDead())
213213
return -1;
214-
//TODO: Option to use XMLRPC over localhost or pipe, currently always use localhost
215-
m_port=m_portalloc.RequestPort();
216-
if(m_port<0)
217-
return -1;
218-
// m_port = -1; //Use XmlRpc over pipe
214+
//TODO: Option to use XMLRPC over localhost or pipe, currently always use pipe
215+
// m_port=m_portalloc.RequestPort(); //TODO: Request port limited to 3 ports
216+
// if(m_port<0)
217+
// return -1;
218+
m_port = -1; //Use XmlRpc over pipe
219219
//TODO: get the command and working dir from config
220220
#ifdef __WXMSW__
221221
// wxString cmd=_T("cmd /c interp.py ")+wxString::Format(_T(" %i"),m_port); //TODO: this could have process destruction issues on earlier version of wxWidgets (kills cmd, but not python)
@@ -338,6 +338,11 @@ void PythonInterpCtrl::OnPyNotify(XmlRpcResponseEvent& event)
338338
//Otherwise it's an error (-1 syntax error, -2 statement incomplete, -3 code already running
339339
m_codectrl->Enable();
340340
m_codectrl->SetFocus();
341+
} else
342+
{
343+
if (event.GetState()==XMLRPC_STATE_REQUEST_FAILED)
344+
Manager::Get()->GetLogManager()->LogError(_T("Error parsing response from python interpreter console\n")+wxString(event.GetResponse().toXml().c_str(),wxConvUTF8));
345+
//Maybe show a popup?
341346
}
342347
//TODO: If not in error and more input available, need to call RunCode("cont")
343348
}

‎PythonInterpreter/python/interp.py‎

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@
1313

1414
isz=struct.calcsize('I')
1515

16+
DEBUG=False
17+
18+
if DEBUG:
19+
f = open(os.path.join(os.path.expanduser("~"),'pyinterp.log'),'w')
20+
def logmsg(msg,*kargs):
21+
f.write(str(msg))
22+
f.write('\n')
23+
for x in kargs:
24+
f.write('~~~'+str(kargs))
25+
f.write('\n')
26+
f.flush()
27+
else:
28+
def logmsg(msg,*kargs):
29+
return
30+
31+
1632
class XmlRpcPipeServer:
1733
'''
1834
A simple XMLRPC server implementation that uses the stdin, stdout
@@ -23,8 +39,6 @@ def __init__(self):
2339
self.fn_dict={}
2440
self.inpipe=o_stdin
2541
self.outpipe=o_stdout
26-
sys.stdout=open(os.devnull,'wb')
27-
sys.stderr=open(os.devnull,'wb')
2842

2943
def register_function(self,fn,name):
3044
self.fn_dict[name]=fn
@@ -42,8 +56,7 @@ def handle_request(self):
4256
try:
4357
args,name = xmlrpclib.loads(call_xml)
4458
result = self.__call__(name, *args)
45-
if not isinstance(result,tuple):
46-
result=(result,)
59+
result=(result,) #response must always be a length 1 tuple
4760
except:
4861
import traceback
4962
result ='Error running call '+name+'\n'+call_xml+'\n'
@@ -52,6 +65,7 @@ def handle_request(self):
5265
try:
5366
res_xml = bytes(xmlrpclib.dumps(result, methodresponse=True))
5467
except:
68+
import traceback
5569
res_xml = bytes(xmlrpclib.dumps('Method result of length %i could not be converted to XML'%(len(res_xml)), methodresponse=True))
5670
size = len(res_xml)
5771
self.outpipe.write(struct.pack('I',size))
@@ -63,18 +77,6 @@ def __call__(self,name,*args):
6377

6478

6579

66-
DEBUG=False
67-
68-
if DEBUG:
69-
def logmsg(msg,*kargs):
70-
print >>sys.__stdout__,msg,
71-
for x in kargs:
72-
print >>sys.__stdout__,x,
73-
print >>sys.__stdout__,''
74-
else:
75-
def logmsg(msg,*kargs):
76-
return
77-
7880
class datastore:
7981
def __init__(self):
8082
self.data=''
@@ -84,7 +86,7 @@ def write(self,textstr):
8486
self.lock.acquire()
8587
self.data=self.data+textstr
8688
self.lock.release()
87-
def flush(self):
89+
def flush(self): ##TODO: Probably don't really want to do this!! (Clears out the data in the stream)
8890
self.lock.acquire()
8991
self.data=''
9092
self.lock.release()
@@ -94,18 +96,6 @@ def read(self,size=None):
9496
self.data=''
9597
self.lock.release()
9698
return data
97-
if size:
98-
self.lock.acquire()
99-
while 1:
100-
if(len(self.data)>=size):
101-
line=self.data[:size]
102-
self.data=self.data[size:]
103-
self.lock.release()
104-
logmsg('received',size,' chars of text:',line)
105-
return line
106-
self.inputrequest=True
107-
self.lock.wait()
108-
self.inputrequest=False
10999
def readline(self):
110100
#check data for a full line (terminated by \n or EOF(?))
111101
#if the line is there, extract it and return
@@ -120,7 +110,7 @@ def readline(self):
120110
line=self.data[:ind+1]
121111
self.data=self.data[ind+1:]
122112
self.lock.release()
123-
logmsg('received line of text:',line)
113+
logmsg('stdin: received line of text:',line)
124114
return line
125115
self.inputrequest=True
126116
self.lock.wait()
@@ -156,14 +146,16 @@ def queue_code(self,eval_str):
156146
return True
157147
else:
158148
return False
149+
def write(self, data):
150+
self._stderr.write(data)
159151
def main_loop(self):
160152
while self._running: #runs the eval_str queued by the server, then waits for the next eval_str. the loop ends when the server requests exit
161153
try:
162154
if self.eval_str!='':
163-
logmsg('running code ',self.eval_str)
155+
logmsg('interp: running code ',self.eval_str)
164156
try:
165157
self.runsource(self.eval_str+'\n')
166-
logmsg('ran code')
158+
logmsg('interp: ran code')
167159
except KeyboardInterrupt:
168160
print 'Keyboard Interrupt'
169161
except:
@@ -215,7 +207,7 @@ def end(self):
215207
self.lock.release()
216208
return self.cont(None)
217209
def run_code(self,eval_str,stdin):
218-
logmsg("compiling code")
210+
logmsg("compiling code",eval_str,stdin)
219211
try:
220212
cobj=code.compile_command(eval_str)
221213
except:
@@ -242,7 +234,6 @@ def cont(self,stdin):
242234
result=(int(self.interp._runningeval),self.interp._stdout.read(),self.interp._stderr.read(),self.interp._stdin.HasInputRequest())
243235
logmsg('returning result ',result)
244236
return result
245-
#return status, stdout, stderr
246237
def break_code(self):
247238
if self.interp._runningeval:
248239
raise KeyboardInterrupt

0 commit comments

Comments
 (0)