Использовать предоставленный пользователем код Python во время выполнения - PullRequest
1 голос
/ 02 августа 2011

Я разрабатываю систему, которая оперирует (произвольными) данными из баз данных. Данные могут нуждаться в некоторой предварительной обработке, прежде чем система сможет работать с ними. Чтобы позволить пользователю указать, возможно, сложные правила, я дал пользователю возможность вводить код Python, который используется для выполнения этой задачи. Система чистого Python.

Мой план состоит в том, чтобы представить таблицы и столбцы как переменные и позволить пользователю делать все, что может делать Python (включая доступ к стандартным библиотекам). Теперь к моей проблеме:

Как взять строку (введенную пользователем), скомпилировать ее в Python (после добавления кода для предоставления входных данных) и получить вывод. Я думаю, что самым простым способом было бы использовать введенные пользователем данные в теле метода и получить возвращаемое значение этой функции в качестве моих новых данных.

Возможно ли это? Если да, то как? Неважно, что пользователь может ввести вредоносный код, поскольку худшее, что может случиться, это то, что он испортил свою собственную систему, что, к счастью, не моя проблема;)

Ответы [ 6 ]

4 голосов
/ 02 августа 2011

Python предоставляет оператор exec(), который должен делать то, что вы хотите.Вы захотите передать переменные, которые вы хотите использовать в качестве второго и / или третьего аргументов функции (глобальные и локальные соответственно), поскольку они управляют средой, в которой запускается exec.

Дляпример:

env = {'somevar': 'somevalue'}
exec(code, env)

В качестве альтернативы, execfile() можно использовать аналогичным образом, если выполняемый код хранится в своем собственном файле.

Если у вас есть только одно выражение, которое вы хотите выполнить, вы также можете использовать eval.

2 голосов
/ 02 августа 2011

Возможно ли это?

Если это не связано с путешествием во времени, антигравитацией или вечным движением, ответом на этот вопрос всегда будет «ДА». Вам не нужно спрашивать это.

Правильный путь состоит в следующем.

Вы создаете фреймворк с некоторыми удобными библиотеками и пакетами.

Вы создаете несколько примеров приложений, которые реализуют это требование: «Перед тем, как система сможет работать с ними, может потребоваться предварительная обработка данных».

Вы пишете документацию о том, как это приложение импортирует и использует модули из вашей инфраструктуры.

Вы передаете среду, примеры приложений и документацию пользователям, чтобы они могли создавать эти приложения.

Не тратьте время на «возьмите строку (введенную пользователем), скомпилируйте ее в Python (после добавления кода для предоставления входных данных) и получите вывод».

Пользователь должен писать такие приложения.

from your_framework import the_file_loop
def their_function( one_line_as_dict ):
    one_line_as_dict['field']= some stuff
the_file_loop( their_function )

Это может быть целая программа.

Вам нужно будет написать the_file_loop, что будет выглядеть примерно так.

def the_file_loop( some_function ):
    with open('input') as source:
        with open('output') as target:
            for some_line in source:
                the_data = make_a_dictionary( some_line )
                some_function( the_data )
                target.write( make_a_line( the_data ) )

Создавая фреймворк и позволяя пользователям писать свои собственные программы, вы будете намного довольны результатами. Меньше магии.

1 голос
/ 02 августа 2011

2 варианта:

  • Вы берете его ввод и помещаете его в файл, затем выполняете его.
  • Вы используете exec ()
0 голосов
/ 02 августа 2011

То, что вы на самом деле хотите, это exec, поскольку eval ограничен принятием выражения и возвратом значения. С exec вы можете иметь блоки кода (операторы) и работать с произвольно сложными данными, передаваемыми как глобальные и локальные коды кода.

Затем результат возвращается кодом по некоторому соглашению (например, связывание его с result).

0 голосов
/ 02 августа 2011

хорошо, вы описываете compile()

Но ... я думаю, что я все еще реализовал бы это, используя обычные исходные файлы Python.Добавьте специальное местоположение к пути, скажем, «~ / .myapp / plugins», и просто __import__ все там.Возможно, вы захотите предоставить несколько удобных базовых классов, которые предоставляют интерфейс, который вы пытаетесь предложить, чтобы ваши пользователи могли наследовать их.

0 голосов
/ 02 августа 2011

Если вы просто хотите установить некоторые локальные значения, а затем предоставить оболочку Python, проверьте модуль code .

Вы можете запустить экземпляр оболочки, похожий на оболочку python, а также инициализировать его любыми локальными переменными, которые вы хотите. Это предполагает, что все функции, которые вы хотите использовать, получают значения, встроенные в классы, которые вы передаете как локальные.

Пример:

shell = code.InteractiveConsole({'foo': myVar1, 'bar': myVar2})
...