[pypy-svn] r45449 - in pypy/dist/pypy/translator/sandbox: . test
arigo at codespeak.net
arigo at codespeak.net
Wed Aug 1 18:37:31 CEST 2007
Author: arigo
Date: Wed Aug 1 18:37:29 2007
New Revision: 45449
Added:
pypy/dist/pypy/translator/sandbox/sandlib.py (contents, props changed)
pypy/dist/pypy/translator/sandbox/test/test_sandlib.py (contents, props changed)
Modified:
pypy/dist/pypy/translator/sandbox/sandboxmsg.py
Log:
Start a library meant for the parent process that runs a subprocess
that was translated with --sandbox.
Modified: pypy/dist/pypy/translator/sandbox/sandboxmsg.py
==============================================================================
--- pypy/dist/pypy/translator/sandbox/sandboxmsg.py (original)
+++ pypy/dist/pypy/translator/sandbox/sandboxmsg.py Wed Aug 1 18:37:29 2007
@@ -155,11 +155,58 @@
raise ValueError
return self.value[i:self.pos]
+ def decode(self, argtypes):
+ "NOT_RPYTHON" # optimized decoder
+ v = self.value
+ i = self.pos
+ for t in argtypes:
+ if v[i] != t:
+ raise ValueError
+ end = i + 5
+ if t == "s":
+ length, = struct.unpack("!i", v[i+1:i+5])
+ end += length
+ yield v[i+5:end]
+ elif t == "i":
+ result, = struct.unpack("!i", v[i+1:end])
+ yield result
+ elif t == "I":
+ result, = struct.unpack("!I", v[i+1:end])
+ yield result
+ else:
+ raise ValueError
+ i = end
+ if i != len(v):
+ raise ValueError("more values to decode")
+
+def encode_message(types, values):
+ "NOT_RPYTHON" # optimized encoder for messages
+ chars = ["!"]
+ entries = []
+ if len(types) != len(values):
+ raise ValueError("mismatch in the number of values to encode")
+ for t, val in zip(types, values):
+ chars.append("c")
+ entries.append(t)
+ if t == "s":
+ if not isinstance(val, str):
+ raise TypeError
+ chars.append("i%ds" % len(val))
+ entries.append(len(val))
+ entries.append(val)
+ elif t in "iI":
+ chars.append(t)
+ entries.append(val)
+ else:
+ raise ValueError
+ data = struct.pack(''.join(chars), *entries)
+ return struct.pack("!i", len(data) + 4) + data
+
def timeout_read(f, size, timeout=None):
if size < 0:
raise ValueError("negative size")
if timeout is None:
- return f.read(size)
+ result = f.read(size)
else:
# XXX not Win32-compliant!
assert not sys.platform.startswith('win'), "XXX fix me"
@@ -173,9 +220,11 @@
len(result), timeout, size))
buf = os.read(fd, size - len(result))
if not buf:
- raise EOFError
+ break
result += buf
- return result
+ if len(result) < size:
+ raise EOFError
+ return result
class Timeout(Exception):
pass
Added: pypy/dist/pypy/translator/sandbox/sandlib.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/sandbox/sandlib.py Wed Aug 1 18:37:29 2007
@@ -0,0 +1,110 @@
+"""
+A Python library to execute and communicate with a subprocess that
+was translated from RPython code with --sandbox. This library is
+for the outer process, which can run CPython or PyPy.
+"""
+
+from py.compat import subprocess
+from pypy.translator.sandbox.sandboxmsg import Message, encode_message
+from pypy.translator.sandbox.sandboxmsg import read_message
+
+class SandboxedProc(object):
+ """Base class to control a sandboxed subprocess.
+ Inherit from this class and implement all the do_xxx() methods
+ for the external functions xxx that you want to support.
+ """
+ def __init__(self, args):
+ """'args' should a sequence of argument for the subprocess,
+ starting with the full path of the executable.
+ """
+ self.popen = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ def poll(self):
+ return self.popen.poll()
+
+ def wait(self):
+ return self.popen.wait()
+
+ def handle_forever(self):
+ while True:
+ try:
+ msg = read_message(self.popen.stdout)
+ except EOFError, e:
+ break
+ answer = self.handle_message(msg)
+ self.popen.stdin.write(answer)
+ returncode = self.popen.wait()
+ if returncode != 0:
+ raise OSError("the sandboxed subprocess exited with code %d" % (
+ returncode,))
+
+ def handle_message(self, msg):
+ fn = msg.nextstring()
+ try:
+ argtypes, restypes = self.TYPES[fn]
+ except KeyError:
+ raise IOError("trying to invoke unknown external function %r" % (
+ fn,))
+ handler = getattr(self, 'do_' + fn)
+ answers = handler(*msg.decode(argtypes))
+ if len(restypes) == 0:
+ assert answers is None
+ answers = (0,)
+ elif len(restypes) == 1:
+ answers = (0, answers)
+ else:
+ answers = (0,) + answers
+ return encode_message("i" + restypes, answers)
+
+ TYPES = {
+ "open": ("sii", "i"),
+ "read": ("iI", "s"),
+ "write": ("is", "I"),
+ "close": ("i", "i"),
+ }
+
+
+class SimpleIOSandboxedProc(SandboxedProc):
+ """Control a sandboxed subprocess which is only allowed to read from
+ its stdin and write to its stdout and stderr.
+ """
+ _input = None
+ _output = None
+ _error = None
+
+ 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)
+
+ def do_read(self, fd, size):
+ if fd == 0:
+ if self._input is None:
+ return ""
+ else:
+ return self._input.read(size)
+ raise OSError("trying to read from fd %d" % (fd,))
+
+ def do_write(self, fd, data):
+ if fd == 1:
+ self._output.write(data)
+ return len(data)
+ if fd == 2:
+ self._error.write(data)
+ return len(data)
+ raise OSError("trying to write to fd %d" % (fd,))
Added: pypy/dist/pypy/translator/sandbox/test/test_sandlib.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/sandbox/test/test_sandlib.py Wed Aug 1 18:37:29 2007
@@ -0,0 +1,79 @@
+import os, StringIO
+from pypy.tool.sourcetools import func_with_new_name
+from pypy.translator.sandbox.sandlib import SandboxedProc
+from pypy.translator.sandbox.sandlib import SimpleIOSandboxedProc
+from pypy.translator.interactive import Translation
+
+
+class MySandboxedProc(SandboxedProc):
+
+ def __init__(self, args, expected):
+ SandboxedProc.__init__(self, args)
+ self.expected = expected
+ self.seen = 0
+
+ def _make_method(name):
+ def do_xxx(self, *input):
+ print "decoded from subprocess: %s%r" % (name, input)
+ expectedmsg, expectedinput, output = self.expected[self.seen]
+ assert name == expectedmsg
+ assert input == expectedinput
+ self.seen += 1
+ return output
+ return func_with_new_name(do_xxx, 'do_%s' % name)
+
+ do_open = _make_method("open")
+ do_read = _make_method("read")
+ do_write = _make_method("write")
+ do_close = _make_method("close")
+
+
+def test_lib():
+ def entry_point(argv):
+ fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
+ assert fd == 77
+ res = os.read(fd, 123)
+ assert res == "he\x00llo"
+ count = os.write(fd, "world\x00!\x00")
+ assert count == 42
+ for arg in argv:
+ count = os.write(fd, arg)
+ assert count == 61
+ os.close(fd)
+ return 0
+ t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
+ exe = t.compile()
+
+ proc = MySandboxedProc([exe, 'x1', 'y2'], expected = [
+ ("open", ("/tmp/foobar", os.O_RDONLY, 0777), 77),
+ ("read", (77, 123), "he\x00llo"),
+ ("write", (77, "world\x00!\x00"), 42),
+ ("write", (77, exe), 61),
+ ("write", (77, "x1"), 61),
+ ("write", (77, "y2"), 61),
+ ("close", (77,), 0),
+ ])
+ proc.handle_forever()
+ assert proc.seen == len(proc.expected)
+
+def test_simpleio():
+ def entry_point(argv):
+ print "Please enter a number:"
+ buf = ""
+ while True:
+ t = os.read(0, 1) # 1 character from stdin
+ if not t:
+ raise EOFError
+ if t == '\n':
+ break
+ buf += t
+ num = int(buf)
+ print "The double is:", num * 2
+ return 0
+ t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
+ exe = t.compile()
+
+ proc = SimpleIOSandboxedProc([exe, 'x1', 'y2'])
+ output, error = proc.communicate("21\n")
+ assert output == "Please enter a number:\nThe double is: 42\n"
+ assert error == ""
More information about the pypy-svn
mailing list