首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
V2EX  ›  Python

[求教] 如何实现一个沙盒机制来执行来自字符串的 Python 代码

  •  
  •   Hualin · 2013-07-17 07:06:10 +08:00 · 2394 次点击
    这是一个创建于 2283 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #背景

    手头的项目是关于科学计算的。一个大的数据集,分别由以下几个文件组成:
    + crdArr => N个数据的坐标;
    + labelDict => N个数据的数据属性字典,如 labelDict["some_key"] 里面存着一个长度为N的某种属性的数据,相当于一个 speadsheet,key 作为 header 的索引键;
    + disMtx => N*N 的距离矩阵。

    程序需要根据 labelDict 中的属性用数学表达式从 N 个数据中得到一个子集的 subset Index。每一个 subset index 作用到原有数据集会得到一个子集,这个子集生成一个叫做 collection 的对象然后交给后面的模块。。。

    过去选取子集的代码都是写死在 python 里,用 Numpy (一个科学计算的 python 库,类似 Matlab),后来为了灵活,我在外面写了一个 json 的配置文件,把要设置的参数全部放进去。so far so good。
    问题是,选取子集需要一个可能互相嵌套的数学表达式,比如:
    idx = (labelDict["age"] > 30 && labelDict["age"] < 40) && (labelDict["occupation"] == "teacher")

    这种表达式很容易用 numpy 的语法实现。但是 json object 是一个较为 flat 的结构,我没法在里面实现数学表达式的复杂逻辑和互相嵌套。目前在 json 文件中我只能把选取逻辑做成一个 list,然后一条一条的做逻辑运算,但很显然这种平坦结构的表达无法实现嵌套,可是如果用 json 来写一个数学表达式显然不合适。

    还有一个原因,就是这个项目需要用户在程序执行的时候动态选取一个子集,这就是没法把选取子集的代码写死在 python 里的原因,同样,json 的配置文件也只是一个暂时的策略。我需要一个 GUI 来让用户输入可能的数学表达式来提取他子集需要的数据子集。所以我没法用 GUI 的控件来让用户设定子集选取表达式,而是想叫用户直接在文本框输入这种表达式。问题来了!!

    # 问题
    我觉得我完全没必要自己重新发明一个 正则表达式来 parse 用户输入的表达式,或者导入什么 Excel 的模块来完成这个任务。因为 Numpy 的语法本身就很清晰易懂。

    所以我决定用 Python 自己的 eval 或者 complie 来提取用户在文本框输入的 Numpy 表达式。

    可是,,,

    首先这种方式很不安全,用户可以输入任何 python 代码,虽然我的程序不是网络程序,只是个科学计算软件,但是总觉得不放心。
    理想方式是创建一个沙河机制,用户在文本框输入的 numpy 表达式在一个沙盒中 eval,有限的 namespace,有限的权限。这样基本上限制用户只能输入应该输入的。


    # 实验
    我刚刚做了个实验,写了一个 py 文件:
    arr = array([1,2,3,4])
    ss = sum(arr)

    用 python 直接执行这个文件是不行的,因为 array() 函数不在命名空间中。
    然后我打开 ipython
    $ ipython
    $ from numpy import array
    $ excufile("the_file_I_mentioned_above.py")
    $ print arr
    $ print ss
    以上可以执行,说明 excufile 是不管上下文的,需要我提前配置好上下文环境。

    可问题是,我怎样限制用户在 eval 里面的输入,如何创建一个安全的执行环境?
    第 1 条附言  ·  2013-07-18 00:38:02 +08:00
    搞定了。
    之间有点小插曲,被 python 的参数传递机制给坑了。
    我在 json 的配置文件里定义了个域,之间把 numpy 的表达式写了进去作为一个字符串,然后从程序中 eval 这个字符串,eval 的另一个参数 globals 我直接讲 labelDict (字典类型)传了进去。可是程序总是执行的不对。在该函数调用的外面,有一个变量也是 labelDict 是原始。我发现一个坑爹的现象。

    在 eval(subsetString, labelDict) 后 labelDict 的类型从原来单纯的 数据字典,变成了 python 全局环境字典。anyway,后来我发现,在这个函数调用过后,labelDict 在该函数调用支出的 父函数的类型也发生了突变。
    后来我改为 eval(subsetString, labelDict.copy()) 便没事了。

    昨天看了一片文章,说 python 函数参数突变类型,今天就遇上了类似的坑,真是好险。。。
    2 回复  |  直到 1970-01-01 08:00:00 +08:00
        1
    dndx   2013-07-17 09:10:27 +08:00 via iPad   ♥ 1
    请参考 http://docs.python.org/2/reference/simple_stmts.html#exec
    或者
    http://docs.python.org/2/library/functions.html#eval

    这两种方法都可以指定执行时使用的 globals 和 locals 。直接 pass 一个 dict 过去即可避免污染当前环境。
        2
    dreampuf   2013-07-18 01:34:11 +08:00
    @dndx 防君子可以,防小人可不能这么马虎 http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2674 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 20ms · UTC 12:41 · PVG 20:41 · LAX 05:41 · JFK 08:41
    ♥ Do have faith in what you're doing.