Question

I'm doing code generation and I end up with a string of source that looks like this:

Source

import sys
import operator

def add(a,b):
    return operator.add(a,b)

def mul(a,b):
    return operator.mul(a,b)

def saveDiv(a,b):
    if b==0:
        return 0
    else:
        return a/b

def subtract(a,b):
    return operator.sub(a,b)

def main(y,x,z):
   y = int(y)
    print y
    x = int(x)
    print x
    z = int(z)
    print z
    ind = lambda y,x,z: mul(saveDiv(x, add(z, z)), 1)
    return ind(y,x,z)

print main(**sys.argv)""

Execution

When I'm executing code using exec() and then piping it through stdoutIO()

Working

args={'x':"1",'y':"1",'z':"1"}
source = getSource()
sys.argv = args
with stdoutIO() as s:
    exec source
s.getvalue

Not Working

class Coder():

    def start(self):

        args={'x':"1",'y':"1",'z':"1"}

        source = getSource()
        sys.argv = args

        with stdoutIO() as s:
            exec source

        return s.getvalue

print "out:", Coder().start()

And the stdoutIO() is implemented like this:

class Proxy(object):

    def __init__(self,stdout,stringio):
        self._stdout = stdout
        self._stringio = stringio

    def __getattr__(self,name):
        if name in ('_stdout','_stringio','write'):
            object.__getattribute__(self,name)
        else:
            return getattr(self._stringio,name)

    def write(self,data):
         self._stdout.write(data)
         self._stringio.write(data)

    @contextlib.contextmanager
    def stdoutIO(stdout=None):
        old = sys.stdout
        if stdout is None:
            stdout = StringIO.StringIO()
        sys.stdout = Proxy(sys.stdout,stdout)
        yield sys.stdout
        sys.stdout = old

Problem

If I execute the execution code outside of the class everything works however when I run it inside a class it breaks with this error. How can I fix it or avoid this problem?

  File "<string>", line 29, in <module>
  File "<string>", line 27, in main
  File "<string>", line 26, in <lambda>
  NameError: global name 'add' is not defined

Thanks

Was it helpful?

Solution

When you run exec expression, it executes the code contained in expression in the current scope (see here). Apparently inside a class, the function in your expression are dropping out of scope before main is run. I honestly have no idea why (it seems to me like it should work) but maybe someone can add a complete explanation in a comment.

Anyway, if you specifically provide a scope for the expression to be evaluated in, (which is good practice anyway so that you don't pollute your namespace), it works fine inside the class.

So, replace the line:

    exec source

with

    exec source in {}

and you should be right!

Here we provide an empty dictionary as a the globals() and locals() dctionaries during the evaluation of your expression. You can keep this dictionary if you want, or let it be garbage collected immediately as I have demonstrated in my code. This is all explained in the exec documentation in the link above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top