最近一直在捣鼓Python,老想学别人在自己的SolidMCP之类搞一个Python Interpreter。
起初觉得很高深,就直接把将某开源软件基于Qt的Python Console实现剥离开来,成功移植到SolidMCP内。
在这个过程中发现,其实写一个蹩脚的Python Interpreter并不是太难,Piaoger决定沉下心来,研究一把,并把研究成果记录下来如下:
>> 如何写一个Python Interpreter
可以先用Python来写一个Prototype,再用C++翻译过来就是啦。
importsysimportosimportcode
redirectedOutput= r'C:\RedirectedPythonConsole.txt'
#Redirected Channel
classMyConsole:defwrite(self, input):
myFile= file(redirectedOutput, 'a')
output= '\nPython>' +input
myFile.write(output)
myFile.close()#Redirect stdout to MyConsole
defaultStdOut =sys.stdout
myConsole=MyConsole()
sys.stdout=myConsoleifos.path.exists(redirectedOutput):
os.remove(redirectedOutput)#Compile input code to code object#Run Code Object
inputCode = '2+3'interpreter=code.InteractiveInterpreter()
codeObject=interpreter.compile(inputCode)
interpreter.runcode(codeObject)
sys.stdout= myConsole
>>C++版本原型
//Demonstrates how to use Interactive Interpreter and Interactive Console
voidtestInterpreter()
{
Py_Initialize();//#load code module to use InteractiveInterpreter and InteractiveConsole//# Utilities used to emulate Python's interactive interpreter//# InteractiveConsole closely emulates the behavior of the interactive Python interpreter//# InteractiveConsole builds on InteractiveInterpreter//import code module
PyObject* module = PyImport_ImportModule("code");//Construct InteractiveInterpreter from code module
PyObject* func = PyObject_GetAttrString(module, "InteractiveInterpreter");
PyObject* args = Py_BuildValue("()");
PyObject* interpreter =PyEval_CallObject(func,args);//Provide input as code
const char* source = "2+3";
PyObject* sourceArgs = Py_BuildValue("(s)", source);
PyObject* compilefunc = PyObject_GetAttrString(interpreter, "compile");
PyObject* codeObject =PyEval_CallObject(compilefunc, sourceArgs);//run compiled bytecode
PyObject* mainModule = PyImport_AddModule("__main__");
PyObject* dict =PyModule_GetDict(mainModule);
PyObject* presult = PyEval_EvalCode((PyCodeObject*)codeObject, dict, dict);//Finally, release everything by decrementing their reference counts.//Py_DECREF(mainModule);
Py_DECREF(presult);
Py_DECREF(dict);
Py_DECREF(codeObject);
Py_DECREF(compilefunc);
Py_DECREF(sourceArgs);
Py_DECREF(interpreter);
Py_DECREF(args);
Py_DECREF(func);
Py_Finalize();
}
基本上原理就是这样的,其余的事情不外乎也搞一个sys.stdout的重定向,然后就是处理TextEdit控件的事件啦。
>> 利用InteractiveConsole直接用Python写
Python提供了一个InteractiveConsole帮我们来干这个事情,这玩意其实也是继承自InteractiveInterpreter.
那些基于PyQt的Python Console大抵就是这样干的。
# -----------------------------------------------------------------------------------------------
# Copy from ActiveState Code Recipes
# http://code.activestate.com/recipes/355319-using-codeinteractiveconsole-to-embed-a-python-she/
# -----------------------------------------------------------------------------------------------
importsysimportcodefrom code importInteractiveConsoleclassFileCacher:"Cache the stdout text so we can analyze it before returning it"
def __init__(self): self.reset()def reset(self): self.out =[]defwrite(self,line): self.out.append(line)defflush(self):
output= '\n'.join(self.out)
self.reset()returnoutputclassShell(InteractiveConsole):"Wrapper around Python that can filter input/output to the shell"
def __init__(self):
self.stdout=sys.stdout
self.cache=FileCacher()
InteractiveConsole.__init__(self)return
def get_output(self):
sys.stdout =self.cachedef return_output(self):
sys.stdout =self.stdoutdefpush(self,line):
self.get_output()#you can filter input here by doing something like
#line = filter(line)
InteractiveConsole.push(self,line)
self.return_output()
output=self.cache.flush()#you can filter the output here by doing something like
#output = filter(output)
print output #or do something else with it
return
if __name__ == '__main__':
sh=Shell()
sh.interact()