在Python中创建一个简单的脚本语言

Man*_*y D 10 python xml scripting

我正在创建一个可以监视和操作消息流的GUI应用程序.我正在尝试创建一个简单的方法让用户编写一些此功能的脚本,我正在寻找可能的候选人.最初我想使用XML,因为它可以自然地处理嵌入式代码:

<if>
   <condition>
      <recv>
         <MesgTypeA/>
      </recv>
   </condition>
   <loop count=10>
      <send>
         <MesgTypeB>
            <param1>12</param1>
            <param2>52</param2>
         </MesgTypeB>
      </send>
   </loop>
</if>
Run Code Online (Sandbox Code Playgroud)

对于解析,我计划使用ElementTree,只是在代码中构建状态.编写和读取XML并不是最容易的事情,特别是因为我不能假设脚本的编写者会有任何经验.我想知道是否有人在Python中更容易读/写和处理.我查看了JSON,但因为它是一个脚本,所以顺序很重要.

有人可以建议任何可能的选择

谢谢.

Sku*_*del 16

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!
Run Code Online (Sandbox Code Playgroud)

exec让我们你是明确你想要从主机环境什么通过两个可选参数揭露localsglobals.

在这个例子中,我明确了脚本可以访问的全局变量.请注意,我可以在此处"创建"变量,或者为现有函数指定其他名称.这是一个指向功能和数据的字典.

>>> 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()")
Run Code Online (Sandbox Code Playgroud)

使用包含两个函数的globals调用它,指向已存在的函数给出:

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

现在,当我省略时secret,脚本无法再找到它.

>>> 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
Run Code Online (Sandbox Code Playgroud)

在这里,我重新定义secret所有:

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

正如lazyr在评论中提到的那样存在安全问题.上面的例子让脚本几乎可以做它想要的东西.在某些情况下,这是不可接受的.

有些东西可以阻止它:

  • Neuter __builtins__,只允许"白名单"内置功能.
  • 难以导入模块.

例如,这里是你如何bork 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.
Run Code Online (Sandbox Code Playgroud)

从这以下我们可以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.
Run Code Online (Sandbox Code Playgroud)

但是,需要注意的是,这会导致所有进口后发生.将其替换为允许您导入白名单模块的版本可能更好.

最后,我不确定这会完全保护你.一些狡猾的人可能会很好地规避它.但至少应该劝阻最明目张胆的违法行为.


Mar*_*nen 5

您可以使用pyparsing定义自己的脚本语言语法.