[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!