需求
以我的bot来说:
Bot:
├─config
├─core
├─modules
│ ├─required
│ │ ├─BotManager
│ │ ├─BotStatus
│ │ ├─ExceptionCatch
│ │ ├─...
│ └─self
│ ├─Modules1
│ ├─Modules2
│ ├─...
├─resources
├─service
└─utils
模块统一放在modules下,required是bot运行必须的模块,self是可卸载安装的模块。
当有模块涉及到需要数据库的操作时,我设想的是,每个模块的数据源不放在一起,如果是sqlite的话单独以db.sqlite的形式存放在本地,远程DB的话就不需要考虑存放。
好处是,解耦(大概?),不同数据源可以带来性能提升,迁移维护时可以先关闭某个模块而不是整个程序。
单说实现的话也挺简单,维护一个dict数据,使用模块名作为key,实际DB对象(存放数据源信息)作为value,之后就是建个类生成和管理。问题是模块需要有实际的DB对象,才能进行操作,能不能让模块和数据源建立联系,这样模块不需要自己持有DB,只构建实体类和调用公共CURD方法。
模块统一放在modules下。其中,required是bot运行必须的模块,而self则是可卸载安装的模块。
当某个模块涉及到需要数据库操作时,我设想将该模块的数据源单独存放。如果使用sqlite,则将其存储在本地,并命名为db.sqlite;如果使用远程DB,则无需考虑存储位置。
这样做有以下好处:
- 解耦(大概?)
- 不同数据源可以带来性能提升
- 迁移维护时可以先关闭某个模块而不是整个程序。
具体实现方法如下:
- 维护一个字典数据结构;
- 使用模块名作为键值;
- 使用实际数据库对象作为值;
- 建立一个类来生成和管理字典。
但问题在于:每个模块都需要有实际数据库对象才能进行操作。因此我们需要让每个模块与其对应的数据库建立联系,并且只构建实体类和调用公共CURD方法即可。
思路
模块名与模块所在的文件夹是一一对应的。这也可以让模块的文件夹名与数据源一一对应。当模块内的方法调用公共CURD方法时,它能够从模块文件夹名中找到数据源。核心就是inspect模块:
import inspect
# 从当前调用帧中寻找上一个调用帧的代码所在的文件名
inspect.currentframe().f_back.f_code.co_filename
然后可以写个方法:
import inspect
from pathlib import Path
__current_dir = Path(__file__).parent
def print_caller_info():
last_frame = None
back_frame = None
try:
for _ in range(3):
if not last_frame:
last_frame = inspect.currentframe()
assert last_frame
back_frame = last_frame.f_back
assert back_frame
back_dir = Path(back_frame.f_code.co_filename).parent
if back_dir == __current_dir:
last_frame = back_frame
continue
else:
print(
f"Caller: {back_frame.f_code.co_name} in {back_frame.f_code.co_filename}"
)
print("loop end")
finally:
del last_frame
del back_frame
由于CURD方法的重复包装,有可能上上帧才是真正的模块方法。辨别方法就是对比该方法所在的文件夹和上个帧所在的文件夹。如果相同,则继续对比上一个;不一样则说明该帧为模块的调用帧。
目前仅记录这一用法,不准备实际更新为这种方法,所以可能存在未知的错误。