Запустите exec () без подключения к основной программе на Python 3 - PullRequest
0 голосов
/ 15 октября 2018

Я пытаюсь создать Python 3 IDE и текстовый редактор на Python, чтобы узнать больше о tkinter.В нем (потому что - это IDE) я пытаюсь запустить код, введенный пользователем.Лучший способ, который я мог найти, это использовать exec().Это работает, как показано на рисунке ниже:

>>> 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.stdin, sys.stdout и sys.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.

Я попытался использовать пустой dict для глобалов, и я оставил местных жителей пустыми.Кажется, это работает с первого взгляда.

>>> 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.stdin, sys.stdout и sys.stderr.exec(source, blank_dict) - это путь или есть лучший способ сделать это?

1 Ответ

0 голосов
/ 18 октября 2018

После долгих поисков я нашел ответ на свой вопрос.

Ответ находится в модуле code.В документации python для модуля кода указано:

Модуль кода предоставляет средства для реализации циклов read-eval-print в Python.

В основном инструменты для симуляции выполнения кода.Функция, которую предоставляет модуль кода, не только помогает с циклом read-eval-print, но также может запускать код без подключения к основной программе.Возьмем для примера эту программу с именем 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')

И это все!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...