中止对python交互式控制台的评估
作者:互联网
我正在编写自己的python代码编辑器和终端以取乐,并在现有程序中实现它以增加可写性.
现在,我发现了一个问题,我不知道如何在代码运行后停止评估.那怎么办?
这是我的实现:
import code
import contextlib
import sys
from io import StringIO
import copy
@contextlib.contextmanager
def capture():
oldout,olderr = sys.stdout, sys.stderr
try:
out=[StringIO(), StringIO()]
sys.stdout,sys.stderr = out
yield out
finally:
sys.stdout,sys.stderr = oldout, olderr
out[0] = out[0].getvalue()
out[1] = out[1].getvalue()
class PythonTerminal(code.InteractiveConsole):
def __init__(self, shared_vars):
self.shared_vars_start = copy.deepcopy(shared_vars)
self.shared_vars = shared_vars
super().__init__(shared_vars)
self.out_history = []
def run_code(self,code_string):
with capture() as out:
self.runcode(code_string)
self.out_history.append(out)
return out
def restart_interpreter(self):
self.__init__(self.shared_vars_start)
def stop(self):
raise NotImplementedError
if __name__ == '__main__':
a = range(10)
PyTerm = PythonTerminal({'Betrag': a})
test_code = """
for i in range(10000):
for j in range(1000):
temp = i*j
print('Finished'+str(i))
"""
print('Starting')
t = threading.Thread(target=PyTerm.run_code,args=(test_code,))
t.start()
PyTerm.stop()
t.join()
print(PyTerm.out_history[-1]) # This line should be executed immediately and contain an InterruptError
目的是停止评估,但解释器仍然有效,例如ctrl c.
解决方法:
我认为您无法轻易杀死Python中的线程.但是,您可以杀死一个multiprocessing.Process.因此,您可以使用单独的过程在控制台中执行代码并通过multiprocessing.Queue与之通信.为此,我实现了一个TerminalManager类,该类可以在单独的进程中执行PythonTerminal.run_code并杀死它.请参阅下面的修改后的代码.对此的一个主要缺点是,InteractiveConsole的本地语言在两次调用之间不会保持不变.我添加了一个将本地人存储到搁置文件中的黑客(这可能很糟糕).想到的最快的事情.
import code
import contextlib
import sys
from io import StringIO
import copy
import threading
import multiprocessing
import json
import shelve
class QueueIO:
"""Uses a multiprocessing.Queue object o capture stdout and stderr"""
def __init__(self, q=None):
self.q = multiprocessing.Queue() if q is None else q
def write(self, value):
self.q.put(value)
def writelines(self, values):
self.q.put("\n".join(str(v) for v in values))
def read(self):
return self.q.get()
def readlines(self):
result = ""
while not self.q.empty():
result += self.q.get() + "\n"
@contextlib.contextmanager
def capture2(q: multiprocessing.Queue):
oldout,olderr = sys.stdout, sys.stderr
try:
qio = QueueIO(q)
out=[qio, qio]
sys.stdout,sys.stderr = out
yield out
finally:
sys.stdout,sys.stderr = oldout, olderr
class PythonTerminal(code.InteractiveConsole):
def __init__(self, shared_vars):
self.shared_vars_start = copy.deepcopy(shared_vars)
self.shared_vars = shared_vars
super().__init__(shared_vars)
self.out_history = []
def run_code(self,code_string, q):
# retrieve locals
d = shelve.open(r'd:\temp\shelve.pydb')
for k, v in d.items():
self.locals[k] = v
# execute code
with capture2(q) as out:
self.runcode(code_string)
# store locals
for k, v in self.locals.items():
try:
if k != '__builtins__':
d[k] = v
except TypeError:
pass
d.close()
def restart_interpreter(self):
self.__init__(self.shared_vars_start)
class TerminalManager():
def __init__(self, terminal):
self.terminal = terminal
self.process = None
self.q = multiprocessing.Queue()
def run_code(self, test_code):
self.process = multiprocessing.Process(
target=self.terminal.run_code,args=(test_code, self.q))
self.process.start()
def stop(self):
self.process.terminate()
self.q.put(repr(Exception('User interupted execution.')))
def wait(self):
if self.process.is_alive:
self.process.join()
while not self.q.empty():
print(self.q.get())
if __name__ == '__main__':
import time
a = range(10)
PyTerm = PythonTerminal({'Betrag': a})
test_code = """
import time
a = 'hello'
for i in range(10):
time.sleep(0.2)
print(i)
print('Finished')
"""
mgr = TerminalManager(PyTerm)
print('Starting')
mgr.run_code(test_code)
time.sleep(1)
mgr.stop()
mgr.wait()
test_code = """
import time
_l = locals()
print('a = {}'.format(a))
for i in range(10):
time.sleep(0.1)
print(i)
print('Finished')
"""
print('Starting again')
mgr.run_code(test_code)
mgr.wait()
标签:standard-library,python 来源: https://codeday.me/bug/20191111/2021501.html