Создание простого языка сценариев в Python

Я создаю приложение GUI, которое может контролировать поток сообщений. Я пытаюсь создать простое средство, чтобы позволить пользователю script некоторые из этих функций, и я ищу возможных кандидатов. Первоначально я хотел использовать XML, поскольку он может, естественно, позаботиться о встроенном коде:

<if>
   <condition>
      <recv>
         <MesgTypeA/>
      </recv>
   </condition>
   <loop count=10>
      <send>
         <MesgTypeB>
            <param1>12</param1>
            <param2>52</param2>
         </MesgTypeB>
      </send>
   </loop>
</if>

Для разбора я планировал использовать ElementTree и просто строить состояния из кода. Написание и чтение XML - это не самая простая вещь, тем более что я не могу предположить, что авторы script будут иметь какой-то опыт. Мне было интересно, есть ли у кого-нибудь альтернативы, которые легче читать/писать и обрабатывать в Python. Я заглянул в JSON, но потому что это script, порядок имеет значение.

Может ли кто-нибудь предложить какие-либо возможные альтернативы?

Спасибо.

Ответ 1

Как насчет Python сам?

Например:

>>> import code
>>> def host_func():
...     print("Hello old chap!")
...
>>> c = code.compile_command("print(\"Script says hello!\"); host_func()")
>>> exec(c)
Script says hello!
Hello old chap!

exec позволяет вам четко указать, что из среды хоста, которую вы хотите открыть через два необязательных параметра locals и globals.

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

>>> import code
>>> def secret():
...     print("What?! I don't even... get out of here.")
...
>>> def public():
...     print("Hello stranger.")
...
>>> c = code.compile_command("secret(); public()")

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

>>> exec(c, {"secret": secret, "public": public})
What?! I don't even... get out of here.
Hello stranger.

Теперь, когда я опускаю secret, script больше не сможет его найти.

>>> exec(c, {"public": public})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<input>", line 1, in <module>
NameError: name 'secret' is not defined

Здесь я переопределяю secret все вместе:

>>> exec(c, {"public": public, "secret":lambda: print("Haha! Doppelganger.")})
Haha! Doppelganger.
Hello stranger.

Как lazyr упоминается в комментариях, есть проблемы с безопасностью. Вышеприведенные примеры позволяют script в значительной степени делать то, что он хочет. В некоторых случаях это неприемлемо.

Есть некоторые вещи, которые можно сделать, чтобы отговорить его:

  • Neuter __builtins__, только разрешить встроенные функции с белым цветом.
  • Устранить импорт модулей.

Например, здесь, как вы утверждаете инструкцию import (в Py2. * builtins есть __builtins__):

>>> import builtins
>>> def no_import(*args, **kwargs):
...     raise ImportError("I cannot let you do that, Dave.")
...
>>> builtins.__import__ = no_import
>>> import os
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in no_import
ImportError: I cannot let you do that, Dave.

Из этого следует, что мы можем передать наш собственный builtins в параметре globals:

>>> import code
>>> evil_code = "import os; import stat; os.chmod(\"passwords.txt\", stat.S_IROT
H);"
>>> compiled = code.compile_command(evil_code)
>>> def no_import(*args, **kwargs):
...    raise ImportError("I cannot let you do that, Dave.")
...
>>> exec(compiled, {"__builtins__": {"__import__": no_import}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<input>", line 1, in <module>
  File "<stdin>", line 2, in no_import
ImportError: I cannot let you do that, Dave.

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

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

Ответ 2

Вы можете определить свой собственный синтаксис языка сценариев с помощью pyparsing.

Ответ 3

Python или, может быть, Lisp, потому что синтаксис легко разобрать.