python 2.7/exec/что не так?

У меня есть этот код, который отлично работает в Python 2.5, но не в 2.7:

import sys
import traceback
try:
    from io import StringIO
except:
    from StringIO import StringIO

def CaptureExec(stmt):
    oldio = (sys.stdin, sys.stdout, sys.stderr)
    sio = StringIO()
    sys.stdout = sys.stderr = sio
    try:
        exec(stmt, globals(), globals())
        out = sio.getvalue()
    except Exception, e:
        out = str(e) + "\n" + traceback.format_exc()
    sys.stdin, sys.stdout, sys.stderr = oldio
    return out

print "%s" % CaptureExec("""
import random
print "hello world"
""")

И я получаю:

string argument expected, got 'str'
Traceback (most recent call last):
  File "D:\3.py", line 13, in CaptureExec
    exec(stmt, globals(), globals())
  File "", line 3, in 
TypeError: string argument expected, got 'str'

person Elias Bachaalany    schedule 06.08.2010    source источник
comment
Небольшие комментарии: стиль Pythonic заключается в использовании TitleCase только для классов, он должен быть captureExec или capture_exec. Кроме того, вы должны специально поймать ImportError в блоке try...except.   -  person Katriel    schedule 06.08.2010


Ответы (2)


io.StringIO сбивает с толку в Python 2.7, потому что он перенесен из мира 3.x байт/строка. Этот код получает ту же ошибку, что и ваша:

from io import StringIO
sio = StringIO()
sio.write("Hello\n")

причины:

Traceback (most recent call last):
  File "so2.py", line 3, in <module>
    sio.write("Hello\n")
TypeError: string argument expected, got 'str'

Если вы используете только Python 2.x, то вообще пропустите модуль io и придерживайтесь StringIO. Если вы действительно хотите использовать io, измените импорт на:

from io import BytesIO as StringIO
person Ned Batchelder    schedule 06.08.2010
comment
+1 за BytesIO. Я думаю, что большая часть старого кода 2.x будет не очень совместима с 2.7 :/ Похоже, что 2.7 будет скорее ступенькой к 3.x. - person John La Rooy; 06.08.2010

это плохие новости

io.StringIO хочет работать с юникодом. Вы можете подумать, что можете исправить это, поставив u перед строкой, которую хотите напечатать, вот так

print "%s" % CaptureExec("""
import random
print u"hello world"
""")

однако print действительно не работает для этого, поскольку вызывает 2 записи в StringIO. Первый - u"hello world", что нормально, но затем следует "\n"

поэтому вместо этого вам нужно написать что-то вроде этого

print "%s" % CaptureExec("""
import random
sys.stdout.write(u"hello world\n")
""")
person John La Rooy    schedule 06.08.2010