运行 exec() 没有连接到 Python 中的主程序 3

Run exec() without connection to the main program in Python 3

我正在尝试在 python 中创建 python 3 IDE 和文本编辑器以了解有关 tkinter 的更多信息。在其中(因为它 一个 IDE)我试图 运行 用户输入的代码。我能找到的最好方法是使用 exec()。这有效,如下面的 IDLE 所示:

>>> exec(input('PROMPT: '))
PROMPT: print('Hello World') #Entered in by user, this could be anything.
Hello World # <-- Output

然而,exec() 函数知道它周围的环境。

>>> important_variable = 'important value' #Say that this is important.
>>> exec(input('PROMPT: '))
PROMPT: important_variable = 'meaningless value' #In the IDE, user unknowingly re-assigns to a meaningless value
>>> important_variable #look at the value
'meaningless value' #Yes, the value was changed.

这不是我想要的。我不想连接到主程序,除了我输入的值。(例如,将 sys.stdinsys.stdoutsys.stderr 值更改为 tkinter GUI)

我的想法是使用 exec() 函数的扩展使用(带给你 help()):

exec(source, globals=None, locals=None, /)
    Execute the given source in the context of globals and locals.

    The source may be a string representing one or more Python statements
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.

我尝试为全局变量使用一个空的字典,但我将局部变量留空了。乍一看似乎可行。

>>> important_variable = 'important value'
>>> exec_globals = {} #create a black dict of globals
>>> exec(input('PROMPT: '), exec_globals) #pass in the blank dict
PROMPT: important_variable = 'meaningless value' #change to value?
>>> important_variable #look at the value
'important value' #value is kept!

但是,代码 运行 的程序出现在异常中:

>>> exec_globals = {} #create a black dict of globals
>>> exec(input('PROMPT: '), exec_globals) #pass in the blank dict
PROMPT: THIS SHALL CAUSE A ERROR!
Traceback (most recent call last):
  File "<pyshell#288>", line 1, in <module>
    exec(input('PROMPT: '), exec_globals) # <-- YOU CAN SEE THE CODE
  File "<string>", line 1
    THIS SHALL CAUSE A ERROR!
             ^
SyntaxError: invalid syntax

我如何防止这种情况发生并从用户输入的代码中删除与该程序的任何连接。但是,我仍然希望在程序中进行一些控制,例如更改 sys.stdinsys.stdoutsys.stderr 的值。 exec(source, blank_dict) 是正确的方法,还是有更好的方法?

经过大量挖掘,我找到了问题的答案。

答案在code模块中。正如 python documentation on the code module 所述:

The code module provides facilities to implement read-eval-print loops in Python.

基本上是模拟代码的工具运行ning。代码模块提供的功能不仅有助于读取-评估-打印循环,而且还可以 运行 在不连接到主程序的情况下进行编码。以这个名为 runcode.py 的程序为例:

import code

important_variable = 'important value'

program = '\n'.join([
            'for n in range(10):',
            '    print(n)',
            'important_variable = None',
            'def error(): raise Exception()',
            'error()',
            ])

interpreter = code.InteractiveInterpreter()
interpreter.runsource(program, '<FAKE MEANINGLESS FILE NAME>', 'exec')

print()
print(important_variable)

输出如下:

0
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
  File "<FAKE MEANINGLESS FILE NAME>", line 5, in <module> #No connection!
  File "<FAKE MEANINGLESS FILE NAME>", line 4, in error #No connection!
Exception

important value

如果您正在读取真实文件,请将 '<FAKE MEANINGLESS FILE NAME>' 替换为文件的绝对路径。这将为您提供更高级的回溯(错误消息将包括在线内容)

很棒的是,它可以与设置和结束脚本一起使用,如下所示:

import code

program = '\n'.join([
            'for n in range(10):',
            '    print(n)',
            'def error(): raise Exception()',
            'error()',
            ])

interpreter = code.InteractiveInterpreter()

#Setup code. This could change sys.stdin, stdout, etc. and setup variables!
interpreter.runsource('setup_stuff = None', 'whatever name you want', 'exec')

#Run code
interpreter.runsource(program, '<FAKE MEANINGLESS FILE NAME>', 'exec')

#Process Code now that the program is done, this might get the values in local() or something like that.
interpreter.runsource(program, 'whatever name you want', 'exec')

就是这样!