EXEC字典沙箱仅是名字空间隔离,模块和解释器仍共享带来的坑

执行脚本时,报AttributeError: 'Cursor' object has no attribute 'cursor',如下图所示:

一、问题分析:

在实际代码中使用的是:

with db.cursor() as cursor:

显然curcor已经是游标对象,且后续代码中没有再出现cursor.cursor.所以推测是其他地方调用了get_token或者上游函数,有类似xxx.cursor(),但是xxx已经是Cursor对象,不是数据库连接db对象。结合脚本运行平台为并发运行,所以推测是exec 沙箱命名污染导致

二、问题排查

1、通过日志的方式打印出

python 复制代码
def get_token(self):
    with pymysql.connect(host=HOST_MYSQL, port=PORT_MYSQL, user=USER_MYSQL,
                         password=PASSWORD_MYSQL, db=DB_MYSQL) as db:
        print(f"==== db type: {type(db)} ====")  # 正常应该输出 <class 'pymysql.connections.Connection'>
        with db.cursor() as cursor:
            print(f"==== cursor type: {type(cursor)} ====")
            sql = "SELECT value FROM test_iot.name_value where name = %s"
            cursor.execute(sql, (f"{self.env_type}_token",))
            data = cursor.fetchone()
            if not data:
                raise Exception(f"数据库中不存在{self.env_type}环境的token配置,请先手动初始化一条记录")
            return data[0]

重新运行

with pymysql.connect(...) as db: 这一行执行完,as 赋值出来的 db 变量居然是 Cursor 游标对象 ,不是 Connection 连接对象。结合日志推测在脚本别的地方,提前存在一个名叫connect的变量,他被覆盖成db.cursor,从而导致pymysql.connect被偷偷劫持了。

2、原因剖析 :

exec(user_script, param_dict)中param_dict是全局变量,当脚本运行到pymysql.connect,python优先去全局param_dict找pymysql;拿到pymysql对象再找它的connect属性。

3、问题解决

函数内部import pymysql ,绕开全局污染。

不依赖connect的上下文管理器,手动try-finally关闭连接,避开pymysql connect 上下文被污染。

python 复制代码
    def get_token(self):
        import pymysql
        conn = pymysql.connect(host=HOST_MYSQL, port=PORT_MYSQL, user=USER_MYSQL,
                             password=PASSWORD_MYSQL, db=DB_MYSQL)
        try:
            print(f"==== conn type: {type(conn)} ====")
            with conn.cursor() as cursor:
                sql = "SELECT value FROM test_iot.name_value where name = %s"
                cursor.execute(sql, (f"{self.env_type}_token",))
                data = cursor.fetchone()
                if not data:
                    raise Exception(f"数据库中不存在{self.env_type}环境的token配置,请先手动初始化一条记录")
                return data[0]
        finally:
            conn.close()

三、总结

1、pymysql Connection 不能多线程共用

每次进入函数connect,本身是安全的,但是pymysql底层版本bug会出现对象错乱。

2、exec沙箱尽量不要多线程同时调用数据库函数

exec(user_script, param_dict) 动态执行脚本,尽量不要使用全局变量名 db、cursor,所有数据库连接都放在函数内部作为局部变量,减少命名串扰。

补充:

exec沙箱是用Python内置exec函数,传入自定义globals字典,给用户脚本单独分配一套独立全局名字空间,用来隔离变量。

相关推荐
打工仔折腾 AI1 小时前
介入式脑机接口的跨尺度解码与闭环调控:从算法落地到后端工程实践
人工智能·后端·python·深度学习·算法·ai agent 实战
FITA阿泽要努力1 小时前
/goal 命令:给 Agent 设定目标
服务器·人工智能·python·agent
小小张说故事1 小时前
Python 的 dict 为什么插入顺序不会乱?拆开 3.6 的紧凑布局:indices 与 entries 两张表
python
椰萝Yerosius1 小时前
严正声明 | 博主已正式放弃CSDN作为博客平台
python
肖永威2 小时前
python版微电网仿真工具的前端托管选型:从http.server到FastAPI StaticFiles
前端·python·http·fastapi
lg_cool_2 小时前
Python 代码打包
开发语言·python
2601_962885722 小时前
如何用 Python 做 MACD 金叉全市场选股?
开发语言·python
科技研学社2 小时前
服装厂激光开袋机与智能模板机怎么选?核心工序设备深度对比与落地实战
数据库·python
张张123y6 小时前
后端第十四课:Agent 记忆系统(让 Agent 记住上下文和用户,跨会话不健忘)
java·大数据·人工智能·python·agent·记忆