[Tutor] RExec woes
Danny Yoo
dyoo@hkn.eecs.berkeley.edu
Wed, 24 Oct 2001 23:05:32 -0700 (PDT)
On Tue, 23 Oct 2001, Scott Moynes wrote:
> As a quick example, here's approximately what I'm trying to
> accomplish:
>
> >>> r=rexec.RExec()
> >>> m=r.add_module('sys')
> >>> s=StringIO.StringIO()
> >>> m.__dict__['stdin']=s
> >>> r.r_exec('print "something"')
> something
> >>> s.getvalue()
> ''
Quick solution:
###
import rexec, StringIO
r = rexec.RExec()
r.make_delegate_files()
s = StringIO.StringIO()
r.restricted_stdout = s
r.s_exec("import sys")
r.s_exec("sys.stdout.write('hello world')")
###
Rambling, incoherant explanation of what in the world made me do it this
way is below. *grin*
Ok, I've been staring at the rexec code a little bit, and I think I
understand it better now. There's one part of the code that's really
important, in make_delegate_files():
###
def make_delegate_files(self):
s = self.modules['sys']
self.delegate_stdin = FileDelegate(s, 'stdin')
self.delegate_stdout = FileDelegate(s, 'stdout')
self.delegate_stderr = FileDelegate(s, 'stderr')
self.restricted_stdin = FileWrapper(sys.stdin)
self.restricted_stdout = FileWrapper(sys.stdout)
self.restricted_stderr = FileWrapper(sys.stderr)
###
Remember that old trick with swapping sys.stdout with a StringIO()
instance? Well, that's exactly what rexec does. *grin*
Whenever we run a s_exec() or s_eval(), rexec() actually does swap and
save the old sys.stdin/stdout, replace them with their FileWrapped
equivalents, does its restricted execution stuff, and swaps them back.
That's all the stuff that rexec.set_files(), rexec.reset_files(), and
rexec.save_files() is all about.
To make things work, it might seem that the simplest thing to do is to
redefine our RExec()'s restricted_stdout to our own StringIO instance,
like this:
###
import rexec, StringIO
r = rexec.RExec()
s = StringIO.StringIO()
r.restricted_stdout = s
r.s_exec("import sys")
r.s_exec("sys.stdout.write('hello world')")
###
But this has a REALLY subtle bug, having to with rexec.set_files():
###
def set_files(self):
if not hasattr(self, 'save_stdin'):
self.save_files()
if not hasattr(self, 'delegate_stdin'):
self.make_delegate_files()
###
The bug is that, when we first call set_files(), there is no
'delegate_stdin' attribute, since make_delegate_files() hasn't been called
yet. Why this is, I have no clue. The quick fix is to call
make_delegate_files() at least once, before doing any foolery:
###
import rexec, StringIO
r = rexec.RExec()
r.make_delegate_files()
s = StringIO.StringIO()
r.restricted_stdout = s
r.s_exec("import sys")
r.s_exec("sys.stdout.write('hello world')")
###
But that was convoluted. Can anyone with rexec experience explain why
make_delegate_files() isn't called in RExec's constructor?
Hope this helps!