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字典,给用户脚本单独分配一套独立全局名字空间,用来隔离变量。

相关推荐
量化分析码农1 小时前
【Python量化趋势跟踪实战 02】突破买入总被假突破坑:通道系统与入场时机
python
Agent世界1 小时前
多平台登录态巡检:cookie 判据为什么会撒谎
python
量化分析码农2 小时前
【Python量化趋势跟踪实战 01】均线多头排列就是趋势开始吗:过滤与确认
python
用户298698530142 小时前
Python 实战:把代码写入 Word 文档的两种实现方式
后端·python·api
小小张说故事2 小时前
Python 里 pickle 到底存了什么:拆开 __reduce__、__getstate__ 和那串能执行代码的字节码
python
reasonsummer2 小时前
【教学类-150-03】20261108蝴蝶连连看html(一页2个版面、3个版面)
python·html
用户019027581613 小时前
用 Polars 快速分析 A 股 K 线:比 pandas 更快的列式处理
python
用户0332126663673 小时前
使用 Python 轻松加密或解密 Excel 文档
python·excel
CyrusYiVPP4 小时前
手写 MCP Server:给问答机器人接上本地文件工具,Python 实战第 13 期
人工智能·python
特立独行的猫A4 小时前
cj-tauri:用仓颉写桌面与鸿蒙 PC 应用
java·后端·python