Python3.9环境下Django第三方库类型提示错误的补丁方案咨询
问题背景
在Python 3.9的生产环境中使用rest_framework_simple_api_key,运行python manage.py generate_fernet_key命令时触发以下错误:
File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 15, in <module> class AbstractAPIKeyManager(models.Manager): File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 16, in AbstractAPIKeyManager def get_api_key(self, pk: int | str): TypeError: unsupported operand type(s) for |: 'type' and 'type'
错误原因明确:int | str是Python 3.10及以上版本才支持的联合类型语法,Python 3.9无法解析该语法,导致模块导入失败。
补丁方案可行性分析
用猴子补丁解决该问题完全可行,但你之前的补丁实现时机错误:补丁代码是先导入rest_framework_simple_api_key.models模块再修改方法,而模块导入时Python会先解析所有代码,遇到int | str直接触发错误,根本执行不到打补丁的逻辑。必须在模块被加载解析前,修改掉不兼容的语法。
正确解决方法
方法一:运行时修改模块源代码(猴子补丁)
编写补丁代码,在模块加载前替换掉不兼容的类型语法,确保Python 3.9能正常解析模块内容。
- 编写
monkey_patch.py:
import sys import os import importlib.util from importlib.machinery import SourceFileLoader def patch_rest_framework_simple_api_key(): # 定位rest_framework_simple_api_key的安装路径 if 'rest_framework_simple_api_key' in sys.modules: package_path = os.path.dirname(sys.modules['rest_framework_simple_api_key'].__file__) else: spec = importlib.util.find_spec('rest_framework_simple_api_key') package_path = spec.submodule_search_locations[0] models_file = os.path.join(package_path, 'models.py') # 读取文件内容并替换不兼容语法 with open(models_file, 'r', encoding='utf-8') as f: content = f.read() # 添加Union导入语句(如果不存在) if 'from typing import Union' not in content: content = content.replace( 'from django.db import models', 'from typing import Union\nfrom django.db import models' ) # 替换int | str为Union[int, str] content = content.replace('int | str', 'Union[int, str]') # 动态加载修改后的模块 module_name = 'rest_framework_simple_api_key.models' if module_name in sys.modules: del sys.modules[module_name] loader = SourceFileLoader(module_name, models_file) sys.modules[module_name] = loader.load_module() print("rest_framework_simple_api_key 已完成补丁,兼容Python 3.9")
- 修改
manage.py,在最开头执行补丁(确保在任何导入目标模块的代码之前):
import os import sys # 优先执行补丁 from roomroot.monkey_patch import patch_rest_framework_simple_api_key patch_rest_framework_simple_api_key() def main(): """Run administrative tasks.""" settings_module = "roomroot.deployment" if "WEBSITEHOSTNAME" in os.environ else "roomroot.settings" os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module) try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()
方法二:降级到兼容Python 3.9的版本
更简单的方案是直接安装rest_framework_simple_api_key的旧版本,选择不使用int | str语法的发布版本,执行以下命令:
pip install rest_framework_simple_api_key==<兼容版本号>
你可以查看该包的版本历史,找到明确支持Python 3.9的版本(例如v1.6.0及之前的版本,具体以官方版本说明为准)。
为什么之前的补丁无效
你在settings.py、apps.py、manage.py中调用补丁的时机太晚:当这些文件执行时,Django已经开始加载第三方应用模块,rest_framework_simple_api_key.models已经被导入并触发语法错误,补丁代码根本没机会执行。必须在任何导入目标模块的代码之前执行补丁逻辑。
内容的提问来源于stack exchange,提问作者Abha Rani

