Python 与沙盒PyPy解释器交互-secondary.communicate()返回空元组
我正在尝试创建一种从普通(非沙盒)cPython或pypypy脚本中与沙盒pypypy解释器交互的方法 我已经能够按照这些指令编译一个沙盒pypypy解释器,并且我有一个PyPy-c-sandbox文件,该文件与PyPy_interact.py一起工作,以创建一个交互式沙盒解释器 现在我想做一件非常类似的事情,但我不想使用stdin/stdout作为io,而是想使用python脚本与pypy沙盒进程交互。我大部分时间都在工作。我可以将.communicate()函数与cStringIO对象一起用作输入、输出和错误,并从普通python访问这些数据 然而,这是我的问题,当我在同一个实例化的PyPy沙盒对象上第二次调用.communicate()时,我没有得到任何回报。这只是第一次。沟通是有效的。我很困惑为什么会这样,以及如何避开它 我拼凑了一个丑陋的黑客来证明我的问题:Python 与沙盒PyPy解释器交互-secondary.communicate()返回空元组,python,sandbox,pypy,Python,Sandbox,Pypy,我正在尝试创建一种从普通(非沙盒)cPython或pypypy脚本中与沙盒pypypy解释器交互的方法 我已经能够按照这些指令编译一个沙盒pypypy解释器,并且我有一个PyPy-c-sandbox文件,该文件与PyPy_interact.py一起工作,以创建一个交互式沙盒解释器 现在我想做一件非常类似的事情,但我不想使用stdin/stdout作为io,而是想使用python脚本与pypy沙盒进程交互。我大部分时间都在工作。我可以将.communicate()函数与cStringIO对象一起用
import sys, os
import autopath
from pypy.translator.sandbox.sandlib import SimpleIOSandboxedProc
from pypy.translator.sandbox.sandlib import VirtualizedSandboxedProc
from pypy.translator.sandbox.vfs import Dir, RealDir, RealFile
import pypy
LIB_ROOT = os.path.dirname(os.path.dirname(pypy.__file__))
class PyPySandboxedProc(VirtualizedSandboxedProc, SimpleIOSandboxedProc):
argv0 = '/bin/pypy-c'
virtual_cwd = '/tmp'
virtual_env = {}
virtual_console_isatty = True
arguments = ['../goal/pypy-c', '-u']
def __init__(self, executable, arguments, tmpdir=None, debug=True):
self.executable = executable = os.path.abspath(executable)
self.tmpdir = tmpdir
self.debug = debug
super(PyPySandboxedProc, self).__init__([self.argv0] + arguments,
executable=executable)
def build_virtual_root(self):
# build a virtual file system:
# * can access its own executable
# * can access the pure Python libraries
# * can access the temporary usession directory as /tmp
exclude = ['.pyc', '.pyo']
if self.tmpdir is None:
tmpdirnode = Dir({})
else:
tmpdirnode = RealDir(self.tmpdir, exclude=exclude)
libroot = str(LIB_ROOT)
return Dir({
'bin': Dir({
'pypy-c': RealFile(self.executable),
'lib-python': RealDir(os.path.join(libroot, 'lib-python'),
exclude=exclude),
'lib_pypy': RealDir(os.path.join(libroot, 'lib_pypy'),
exclude=exclude),
}),
'tmp': tmpdirnode,
})
# run test
arguments = ['../goal/pypy-c', '-u']
sandproc = PyPySandboxedProc(arguments[0], arguments[1:],
tmpdir=None, debug=True)
#start the proc
code1 = "print 'started'\na = 5\nprint a"
code2 = "b = a\nprint b\nprint 'code 2 was run'"
output, error = sandproc.communicate(code1)
print "output: %s\n error: %s\n" % (output, error)
output, error = sandproc.communicate(code2)
print "output: %s\n error: %s\n" % (output, error)
我真的很想通过某种方式让code2
由同一个sandproc实例运行,但要单独返回它的输入/输出。如果我将所有代码连接在一起并同时运行它,它会工作,但是解析给定输入的输出会有点麻烦
from rpython.translator.sandbox.sandlib import SimpleIOSandboxedProc
您正在PySandboxedProc中导入和扩展SimpleOsandBoxedProc。在source()中,您将看到sandproc.communicate()在子进程终止后发送数据并返回
def communicate(self, input=None):
"""Send data to stdin. Read data from stdout and stderr,
until end-of-file is reached. Wait for process to terminate.
"""
import cStringIO
if input:
if isinstance(input, str):
input = cStringIO.StringIO(input)
self._input = input
self._output = cStringIO.StringIO()
self._error = cStringIO.StringIO()
self.handle_forever()
output = self._output.getvalue()
self._output = None
error = self._error.getvalue()
self._error = None
return (output, error)
在上面的代码中,self.handle_forever()被调用:
def handle_until_return(self):
child_stdin = self.popen.stdin
child_stdout = self.popen.stdout
if self.os_level_sandboxing and sys.platform.startswith('linux'):
# rationale: we wait until the child process started completely,
# letting the C library do any system calls it wants for
# initialization. When the RPython code starts up, it quickly
# does its first system call. At this point we turn seccomp on.
import select
select.select([child_stdout], [], [])
f = open('/proc/%d/seccomp' % self.popen.pid, 'w')
print >> f, 1
f.close()
while True:
try:
fnname = read_message(child_stdout)
args = read_message(child_stdout)
except EOFError, e:
break
if self.log and not self.is_spam(fnname, *args):
self.log.call('%s(%s)' % (fnname,
', '.join([shortrepr(x) for x in args])))
try:
answer, resulttype = self.handle_message(fnname, *args)
except Exception, e:
tb = sys.exc_info()[2]
write_exception(child_stdin, e, tb)
if self.log:
if str(e):
self.log.exception('%s: %s' % (e.__class__.__name__, e))
else:
self.log.exception('%s' % (e.__class__.__name__,))
else:
if self.log and not self.is_spam(fnname, *args):
self.log.result(shortrepr(answer))
try:
write_message(child_stdin, 0) # error code - 0 for ok
write_message(child_stdin, answer, resulttype)
child_stdin.flush()
except (IOError, OSError):
# likely cause: subprocess is dead, child_stdin closed
if self.poll() is not None:
break
else:
raise
returncode = self.wait()
return returncode
可以看出,是一个“while True:”,这意味着该函数将不会返回,直到我们得到一个抛出的异常因此,sandproc.communicate()无法实现您希望它实现的目标
你有两个选择
硬选项是分叉您的主要流程。使用一个进程运行sandproc.interact(),并传递一些os.pipes。并使用另一个进程对所述管道进行读写。这是低效的,因为它需要3个进程(一个用于沙盒应用程序,一个用于主进程,一个用于分叉进程)
简单选项用于覆盖SimpleOsandBoxedProc类中的某些函数。您所要做的就是在pysandboxedproc中重新实现这些函数
def do_ll_os__ll_os_read(self, fd, size):
if fd == 0:
if self._input is None:
return ""
elif (getattr(self, 'virtual_console_isatty', False) or
self._input.isatty()):
# don't wait for all 'size' chars if reading from a tty,
# to avoid blocking. Instead, stop after reading a line.
# For now, waiting at the interactive console is the
# only time that counts as idle.
self.enter_idle()
try:
inputdata = self._input.readline(size) #TODO: THIS IS WHERE YOU HANDLE READING FROM THE SANDBOXED PROCESS
finally:
self.leave_idle()
else:
inputdata = self._input.read(size)
if self.inputlogfile is not None:
self.inputlogfile.write(inputdata)
return inputdata
raise OSError("trying to read from fd %d" % (fd,))
def do_ll_os__ll_os_write(self, fd, data):
if fd == 1:
self._output.write(data) #TODO: THIS IS WHERE YOU WRITE TO THE SANDBOXED PROCESS
return len(data)
if fd == 2:
self._error.write(data)
return len(data)
raise OSError("trying to write to fd %d" % (fd,))
用#TODO:标签注释的行是从沙盒进程注入和读取数据的地方
您需要做的是实现这样的逻辑:写入一些数据(当沙盒进程读取时)、等待响应(直到进程写入)、写入更多数据,然后再次读取响应(当进程第二次写入时)。通信结束,进程退出。看看在sandlib中如何处理_,直到_return被实现,从而了解如何逐段执行。