add module bank
This commit is contained in:
commit
10fc3f7869
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.13
|
||||
251
README.md
Normal file
251
README.md
Normal file
@ -0,0 +1,251 @@
|
||||
# Python Module Bank - SQLite 模块打包系统
|
||||
|
||||
一个将Python模块打包到SQLite数据库,并支持从数据库直接导入模块的工具系统。
|
||||
|
||||
## 🌟 特性
|
||||
|
||||
- **单文件分发** - 将所有模块打包到单个SQLite数据库文件
|
||||
- **源代码保护** - 模块以编译后的字节码形式存储
|
||||
- **动态导入** - 运行时直接从数据库加载模块,无需文件系统
|
||||
- **完整包支持** - 支持包结构和子模块导入
|
||||
- **CLI工具** - 提供完整的命令行接口
|
||||
- **导入钩子** - 无缝集成Python导入系统
|
||||
|
||||
## 📦 安装
|
||||
|
||||
### 从源码安装
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd python-module-bank
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### 依赖要求
|
||||
|
||||
- Python 3.7+
|
||||
- 无需额外依赖(仅使用标准库)
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 创建示例模块
|
||||
|
||||
```python
|
||||
# my_module.py
|
||||
def hello():
|
||||
print("Hello from my_module!")
|
||||
return "success"
|
||||
```
|
||||
|
||||
### 2. 打包模块到数据库
|
||||
|
||||
```python
|
||||
# pack_example.py
|
||||
from module_bank import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite("my_modules.db")
|
||||
packer.pack_module("my_module.py", "my_module")
|
||||
packer.pack_directory("my_package/")
|
||||
```
|
||||
|
||||
### 3. 从数据库导入
|
||||
|
||||
```python
|
||||
from module_bank import PythonToSQLite
|
||||
|
||||
# 安装导入器
|
||||
packer = PythonToSQLite("my_modules.db")
|
||||
finder = packer.install_importer()
|
||||
|
||||
# 现在可以从数据库导入模块了!
|
||||
import my_module
|
||||
import my_package.package_module
|
||||
|
||||
my_module.hello()
|
||||
my_package.package_module.hello()
|
||||
```
|
||||
|
||||
## 📖 详细使用
|
||||
|
||||
### 命令行工具
|
||||
|
||||
```python
|
||||
# 打包模块或目录
|
||||
python -m module_bank.cli pack my_package --db modules.db
|
||||
|
||||
# 列出数据库中的模块
|
||||
python -m module_bank.cli list --db modules.db
|
||||
|
||||
# 安装导入器并进入交互模式
|
||||
python -m module_bank.cli install --db modules.db
|
||||
```
|
||||
|
||||
### 编程接口
|
||||
|
||||
#### 打包模块
|
||||
|
||||
```python
|
||||
from module_bank import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite("modules.db")
|
||||
|
||||
# 打包单个模块
|
||||
packer.pack_module("module.py", "module_name")
|
||||
|
||||
# 打包整个目录(自动识别包结构)
|
||||
packer.pack_directory("my_package/")
|
||||
|
||||
# 验证包结构
|
||||
packer.verify_package_structure()
|
||||
```
|
||||
|
||||
#### 导入模块
|
||||
|
||||
```python
|
||||
from module_bank import PythonToSQLite
|
||||
import sys
|
||||
|
||||
packer = PythonToSQLite("modules.db")
|
||||
|
||||
# 安装导入器到sys.meta_path
|
||||
finder = packer.install_importer()
|
||||
|
||||
# 列出所有可用模块
|
||||
modules = packer.list_modules()
|
||||
for module in modules:
|
||||
print(f"{module['module_name']} {'[包]' if module['is_package'] else ''}")
|
||||
|
||||
# 导入数据库中的模块
|
||||
import my_package
|
||||
import my_package.submodule
|
||||
```
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 核心组件
|
||||
|
||||
```python
|
||||
src/module_bank/
|
||||
├── python_to_sqlite.py # 主打包类
|
||||
├── sqlite_module_importer.py # 数据库存储管理器
|
||||
├── sqlite_meta_path_finder.py # 元路径查找器
|
||||
├── sqlite_module_loader.py # 模块加载器
|
||||
├── cli.py # 命令行接口
|
||||
└── __init__.py # 模块导出
|
||||
```
|
||||
|
||||
### 数据流
|
||||
|
||||
```text
|
||||
1. 打包阶段:
|
||||
.py文件 → 编译为字节码 → 存储到SQLite数据库
|
||||
|
||||
2. 导入阶段:
|
||||
导入请求 → MetaPathFinder查找 → ModuleLoader加载 → 执行模块
|
||||
```
|
||||
|
||||
### 数据库模式
|
||||
|
||||
```sql
|
||||
CREATE TABLE python_modules (
|
||||
module_name TEXT PRIMARY KEY,
|
||||
source_code TEXT, -- 源代码(可选)
|
||||
bytecode BLOB, -- 编译后的字节码
|
||||
is_package BOOLEAN, -- 是否是包
|
||||
metadata TEXT, -- 元数据(JSON格式)
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
```
|
||||
|
||||
## 🔧 高级功能
|
||||
|
||||
### 排除模式
|
||||
|
||||
```python
|
||||
# 打包时排除特定文件
|
||||
packer.pack_directory(
|
||||
"my_project/",
|
||||
exclude_patterns=["*_test.py", "*.pyc", "__pycache__"]
|
||||
)
|
||||
```
|
||||
|
||||
### 元数据存储
|
||||
|
||||
```python
|
||||
# 为模块添加元数据
|
||||
packer.importer.add_module(
|
||||
"my_module",
|
||||
source_code,
|
||||
is_package=False,
|
||||
metadata={"version": "1.0", "author": "me"}
|
||||
)
|
||||
```
|
||||
|
||||
### 混合导入
|
||||
|
||||
```python
|
||||
# 可以同时使用文件系统和数据库导入
|
||||
# 数据库导入器优先级更高
|
||||
import sys
|
||||
from module_bank import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite("modules.db")
|
||||
finder = packer.install_importer() # 插入到meta_path开头
|
||||
|
||||
# 如果需要文件系统优先,可以调整插入位置
|
||||
sys.meta_path.append(finder)
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 安全性
|
||||
|
||||
- 模块字节码直接执行,确保数据库来源可信
|
||||
- 生产环境建议添加代码签名验证
|
||||
|
||||
### 兼容性
|
||||
|
||||
- 字节码不跨Python版本兼容
|
||||
- 不支持C扩展模块
|
||||
- 不支持需要文件系统资源的模块(如__file__依赖)
|
||||
|
||||
### 性能
|
||||
|
||||
- **启动时**:有一次性数据库查询和反序列化开销
|
||||
- **运行时**:与传统导入性能相同(使用sys.modules缓存)
|
||||
- **最佳适用**:长期运行的服务、桌面应用
|
||||
|
||||
### 更新模块
|
||||
|
||||
```python
|
||||
# 重新打包会自动更新
|
||||
packer.pack_module("updated_module.py", "module_name")
|
||||
```
|
||||
|
||||
### 删除模块
|
||||
|
||||
```sql
|
||||
-- 直接从数据库删除
|
||||
DELETE FROM python_modules WHERE module_name = 'module_to_remove';
|
||||
```
|
||||
|
||||
### 备份与恢复
|
||||
|
||||
```bash
|
||||
# 数据库是单个文件,易于备份
|
||||
cp modules.db modules.backup.db
|
||||
|
||||
# 恢复
|
||||
cp modules.backup.db modules.db
|
||||
```
|
||||
|
||||
## 📚 应用场景
|
||||
|
||||
1. 商业软件分发 - 保护源代码知识产权
|
||||
2. 插件系统 - 动态加载数据库中的插件模块
|
||||
3. 教育平台 - 安全分发练习代码
|
||||
4. 微服务 - 打包多个服务模块到单个文件
|
||||
5. 嵌入式系统 - 减少文件系统依赖
|
||||
|
||||
---
|
||||
**注意**: 本工具主要用于模块分发和部署场景,不适合开发阶段的频繁修改。
|
||||
21
pyproject.toml
Normal file
21
pyproject.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "module_bank"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[[tool.uv.index]]
|
||||
default = true
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
|
||||
[build-system]
|
||||
build-backend = "hatchling.build"
|
||||
requires = ["hatchling"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/module_bank"]
|
||||
|
||||
[project.scripts]
|
||||
mb = "module_bank.cli:main"
|
||||
13
scripts/clean_pycache.py
Normal file
13
scripts/clean_pycache.py
Normal file
@ -0,0 +1,13 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# 需要遍历的目录
|
||||
root_dir = "./"
|
||||
# 遍历目录
|
||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||
if "__pycache__" in dirnames:
|
||||
# 获取 __pycache__ 目录的全路径
|
||||
pycache_dir = os.path.join(dirpath, "__pycache__")
|
||||
# 删除目录
|
||||
shutil.rmtree(pycache_dir)
|
||||
print(f"Removed: {pycache_dir}")
|
||||
29
scripts/code_content.py
Normal file
29
scripts/code_content.py
Normal file
@ -0,0 +1,29 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def generate_markdown_from_py_files(directory, output_file):
|
||||
with open(output_file, "w", encoding="utf-8") as md_file:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
# 排除 venv 目录
|
||||
dirs[:] = [d for d in dirs if d != ".venv"]
|
||||
dirs[:] = [d for d in dirs if d != ".vscode"]
|
||||
dirs[:] = [d for d in dirs if d != "scripts"]
|
||||
dirs[:] = [d for d in dirs if d != "build"]
|
||||
for file in files:
|
||||
if file.endswith(".py") or file.endswith(".rs"):
|
||||
file_path = os.path.join(root, file)
|
||||
md_file.write(f"`{file_path}`\n")
|
||||
md_file.write("```python\n")
|
||||
with open(file_path, "r", encoding="utf-8") as py_file:
|
||||
md_file.write(py_file.read())
|
||||
md_file.write("\n```\n\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 指定目录和输出文件名
|
||||
target_directory = sys.argv[1] # 替换为你的目标目录
|
||||
output_markdown_file = "output.md" # 输出的 Markdown 文件名
|
||||
|
||||
generate_markdown_from_py_files(target_directory, output_markdown_file)
|
||||
print(f"Markdown 文件已生成:{output_markdown_file}")
|
||||
11
src/module_bank/__init__.py
Normal file
11
src/module_bank/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from .python_to_sqlite import PythonToSQLite
|
||||
from .sqlite_module_importer import SQLiteModuleImporter
|
||||
from .sqlite_module_loader import SQLiteModuleLoader
|
||||
from .sqlite_meta_path_finder import SQLiteMetaPathFinder
|
||||
|
||||
__all__ = [
|
||||
"PythonToSQLite",
|
||||
"SQLiteModuleImporter",
|
||||
"SQLiteModuleLoader",
|
||||
"SQLiteMetaPathFinder",
|
||||
]
|
||||
66
src/module_bank/cli.py
Normal file
66
src/module_bank/cli.py
Normal file
@ -0,0 +1,66 @@
|
||||
# cli.py
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Python模块SQLite打包工具")
|
||||
subparsers = parser.add_subparsers(dest="command", help="命令")
|
||||
|
||||
# 打包命令
|
||||
pack_parser = subparsers.add_parser("pack", help="打包模块到SQLite")
|
||||
pack_parser.add_argument("source", help="源文件或目录")
|
||||
pack_parser.add_argument("--db", default="modules.db", help="数据库文件路径")
|
||||
pack_parser.add_argument("--name", help="模块名(默认从文件名推断)")
|
||||
|
||||
# 列出命令
|
||||
list_parser = subparsers.add_parser("list", help="列出数据库中的模块")
|
||||
list_parser.add_argument("--db", default="modules.db", help="数据库文件路径")
|
||||
|
||||
# 安装命令
|
||||
install_parser = subparsers.add_parser("install", help="安装SQLite导入器")
|
||||
install_parser.add_argument("--db", default="modules.db", help="数据库文件路径")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "pack":
|
||||
from .python_to_sqlite import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite(args.db)
|
||||
|
||||
if Path(args.source).is_dir():
|
||||
packer.pack_directory(args.source)
|
||||
print(f"已打包目录: {args.source}")
|
||||
else:
|
||||
packer.pack_module(args.source, args.name)
|
||||
print(f"已打包模块: {args.source}")
|
||||
|
||||
elif args.command == "list":
|
||||
from .python_to_sqlite import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite(args.db)
|
||||
modules = packer.list_modules()
|
||||
|
||||
print(f"数据库中的模块 ({args.db}):")
|
||||
for module in modules:
|
||||
package_flag = " [包]" if module["is_package"] else ""
|
||||
print(f" - {module['module_name']}{package_flag}")
|
||||
|
||||
elif args.command == "install":
|
||||
from .python_to_sqlite import PythonToSQLite
|
||||
|
||||
packer = PythonToSQLite(args.db)
|
||||
packer.install_importer()
|
||||
print(f"已安装SQLite导入器,可以从数据库导入模块了!")
|
||||
|
||||
# 保持程序运行以便交互使用
|
||||
import code
|
||||
|
||||
code.interact(local=locals())
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
119
src/module_bank/python_to_sqlite.py
Normal file
119
src/module_bank/python_to_sqlite.py
Normal file
@ -0,0 +1,119 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from .sqlite_module_importer import SQLiteModuleImporter
|
||||
from .sqlite_meta_path_finder import SQLiteMetaPathFinder
|
||||
|
||||
|
||||
class PythonToSQLite:
|
||||
"""Python到SQLite的打包工具"""
|
||||
|
||||
def __init__(self, db_path: str = "python_modules.db"):
|
||||
self.db_path = db_path
|
||||
self.importer = SQLiteModuleImporter(db_path)
|
||||
|
||||
def pack_directory(
|
||||
self,
|
||||
directory_path: str,
|
||||
include_patterns: List[str] = None,
|
||||
exclude_patterns: List[str] = None,
|
||||
):
|
||||
"""打包整个目录 - 修复版"""
|
||||
directory = Path(directory_path)
|
||||
package_name = directory.name # 包名
|
||||
|
||||
if include_patterns is None:
|
||||
include_patterns = ["*.py"]
|
||||
if exclude_patterns is None:
|
||||
exclude_patterns = ["__pycache__", "*.pyc"]
|
||||
|
||||
# 遍历目录
|
||||
for py_file in directory.rglob("*.py"):
|
||||
# 检查排除模式
|
||||
if any(py_file.match(pattern) for pattern in exclude_patterns):
|
||||
continue
|
||||
|
||||
# 检查包含模式
|
||||
if any(py_file.match(pattern) for pattern in include_patterns):
|
||||
# 计算相对于包的路径
|
||||
rel_path = py_file.relative_to(directory)
|
||||
module_parts = list(rel_path.parts)
|
||||
module_parts[-1] = module_parts[-1][:-3] # 去掉.py
|
||||
|
||||
if module_parts[-1] == "__init__":
|
||||
# 这是包
|
||||
if len(module_parts) > 1:
|
||||
# 子包,例如:subpackage/__init__.py
|
||||
module_name = f"{package_name}.{'.'.join(module_parts[:-1])}"
|
||||
else:
|
||||
# 主包,例如:__init__.py
|
||||
module_name = package_name
|
||||
|
||||
print(f"打包包: {module_name} (来自: {py_file})")
|
||||
self.importer.add_module(
|
||||
module_name,
|
||||
py_file.read_text(encoding="utf-8"),
|
||||
is_package=True,
|
||||
)
|
||||
else:
|
||||
# 普通模块
|
||||
module_name = f"{package_name}.{'.'.join(module_parts)}"
|
||||
|
||||
print(f"打包模块: {module_name} (来自: {py_file})")
|
||||
self.importer.add_module(
|
||||
module_name,
|
||||
py_file.read_text(encoding="utf-8"),
|
||||
is_package=False,
|
||||
)
|
||||
|
||||
def pack_module(self, module_path: str, module_name: str = None):
|
||||
"""打包单个模块"""
|
||||
module_path = Path(module_path)
|
||||
|
||||
if module_name is None:
|
||||
module_name = module_path.stem
|
||||
|
||||
with open(module_path, "r", encoding="utf-8") as f:
|
||||
source_code = f.read()
|
||||
|
||||
print(f"打包独立模块: {module_name} (来自: {module_path})")
|
||||
self.importer.add_module(module_name, source_code, is_package=False)
|
||||
|
||||
def install_importer(self):
|
||||
"""安装导入器到sys.meta_path"""
|
||||
finder = SQLiteMetaPathFinder(self.db_path)
|
||||
# 添加到meta_path开头,优先于文件系统查找
|
||||
sys.meta_path.insert(0, finder)
|
||||
return finder
|
||||
|
||||
def list_modules(self):
|
||||
"""列出数据库中的所有模块"""
|
||||
cursor = self.importer.connection.execute(
|
||||
"SELECT module_name, is_package FROM python_modules ORDER BY module_name"
|
||||
)
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
def verify_package_structure(self):
|
||||
"""验证包的完整性"""
|
||||
cursor = self.importer.connection.execute(
|
||||
"SELECT module_name, is_package FROM python_modules"
|
||||
)
|
||||
modules = {row["module_name"]: row["is_package"] for row in cursor.fetchall()}
|
||||
|
||||
print("数据库中的模块结构:")
|
||||
for module_name, is_package in sorted(modules.items()):
|
||||
package_flag = " [包]" if is_package else ""
|
||||
print(f" - {module_name}{package_flag}")
|
||||
|
||||
# 检查包的完整性
|
||||
for module_name, is_package in modules.items():
|
||||
if "." in module_name and not is_package:
|
||||
parent_package = module_name.rsplit(".", 1)[0]
|
||||
if parent_package not in modules:
|
||||
print(
|
||||
f"警告:模块 {module_name} 的父包 {parent_package} 不在数据库中"
|
||||
)
|
||||
elif not modules[parent_package]:
|
||||
print(
|
||||
f"警告:模块 {module_name} 的父包 {parent_package} 不是一个包"
|
||||
)
|
||||
53
src/module_bank/sqlite_meta_path_finder.py
Normal file
53
src/module_bank/sqlite_meta_path_finder.py
Normal file
@ -0,0 +1,53 @@
|
||||
import sqlite3
|
||||
import importlib
|
||||
import importlib.abc
|
||||
import importlib.util
|
||||
from .sqlite_module_loader import SQLiteModuleLoader
|
||||
|
||||
|
||||
class SQLiteMetaPathFinder(importlib.abc.MetaPathFinder):
|
||||
"""SQLite元路径查找器"""
|
||||
|
||||
def __init__(self, db_path: str):
|
||||
self.db_path = db_path
|
||||
self.connection = sqlite3.connect(db_path)
|
||||
self.connection.row_factory = sqlite3.Row
|
||||
# 缓存模块查找结果
|
||||
self.module_cache = {}
|
||||
self._load_module_cache()
|
||||
|
||||
def _load_module_cache(self):
|
||||
"""加载所有模块到缓存"""
|
||||
cursor = self.connection.execute(
|
||||
"SELECT module_name, is_package FROM python_modules"
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
self.module_cache[row["module_name"]] = row["is_package"]
|
||||
|
||||
def find_spec(self, fullname, path=None, target=None):
|
||||
"""查找模块规范"""
|
||||
# 检查模块是否在数据库中
|
||||
if fullname in self.module_cache:
|
||||
# 创建模块规范
|
||||
spec = importlib.util.spec_from_loader(
|
||||
fullname,
|
||||
SQLiteModuleLoader(self.connection, fullname, self.db_path),
|
||||
origin=f"sqlite://{self.db_path}",
|
||||
is_package=self.module_cache[fullname],
|
||||
)
|
||||
return spec
|
||||
|
||||
# 如果不是完整模块名,检查是否可能是包的子模块
|
||||
# 例如:my_package.package_module
|
||||
if "." in fullname:
|
||||
# 检查完整模块名是否在数据库中
|
||||
if fullname in self.module_cache:
|
||||
spec = importlib.util.spec_from_loader(
|
||||
fullname,
|
||||
SQLiteModuleLoader(self.connection, fullname, self.db_path),
|
||||
origin=f"sqlite://{self.db_path}",
|
||||
is_package=self.module_cache[fullname],
|
||||
)
|
||||
return spec
|
||||
|
||||
return None
|
||||
107
src/module_bank/sqlite_module_importer.py
Normal file
107
src/module_bank/sqlite_module_importer.py
Normal file
@ -0,0 +1,107 @@
|
||||
import sqlite3
|
||||
import marshal
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class SQLiteModuleImporter:
|
||||
"""从SQLite数据库导入Python模块的导入器"""
|
||||
|
||||
def __init__(self, db_path: str):
|
||||
self.db_path = db_path
|
||||
self.connection = sqlite3.connect(db_path)
|
||||
self.connection.row_factory = sqlite3.Row
|
||||
self._create_tables()
|
||||
|
||||
def _create_tables(self):
|
||||
"""创建存储模块的数据库表"""
|
||||
self.connection.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS python_modules (
|
||||
module_name TEXT PRIMARY KEY,
|
||||
source_code TEXT,
|
||||
bytecode BLOB,
|
||||
is_package BOOLEAN,
|
||||
metadata TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
self.connection.commit()
|
||||
|
||||
def add_module(
|
||||
self,
|
||||
module_name: str,
|
||||
source_code: str,
|
||||
is_package: bool = False,
|
||||
metadata: Dict = None,
|
||||
):
|
||||
"""添加模块到数据库"""
|
||||
# 编译为字节码
|
||||
bytecode = compile(source_code, f"<sqlite_module:{module_name}>", "exec")
|
||||
bytecode_blob = marshal.dumps(bytecode)
|
||||
|
||||
metadata_str = str(metadata) if metadata else "{}"
|
||||
|
||||
self.connection.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO python_modules
|
||||
(module_name, source_code, bytecode, is_package, metadata)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(module_name, source_code, bytecode_blob, is_package, metadata_str),
|
||||
)
|
||||
self.connection.commit()
|
||||
|
||||
def add_module_from_file(self, file_path: str, module_name: str = None):
|
||||
"""从文件添加模块"""
|
||||
file_path = Path(file_path)
|
||||
|
||||
if module_name is None:
|
||||
# 从文件名推导模块名
|
||||
module_name = file_path.stem
|
||||
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
source_code = f.read()
|
||||
|
||||
self.add_module(module_name, source_code)
|
||||
|
||||
def add_package(self, package_path: str, package_name: str = None):
|
||||
"""添加整个包到数据库"""
|
||||
package_path = Path(package_path)
|
||||
|
||||
if package_name is None:
|
||||
package_name = package_path.name
|
||||
|
||||
# 添加包目录下的所有.py文件
|
||||
for py_file in package_path.rglob("*.py"):
|
||||
# 计算模块名
|
||||
rel_path = py_file.relative_to(package_path)
|
||||
module_parts = list(rel_path.parts)
|
||||
module_parts[-1] = module_parts[-1][:-3] # 去掉.py
|
||||
|
||||
if module_parts[-1] == "__init__":
|
||||
# 包本身
|
||||
module_name = package_name
|
||||
if len(module_parts) > 1:
|
||||
# 子包
|
||||
module_name = f"{package_name}.{'.'.join(module_parts[:-1])}"
|
||||
self.add_module(
|
||||
module_name,
|
||||
py_file.read_text(encoding="utf-8"),
|
||||
is_package=True,
|
||||
)
|
||||
else:
|
||||
# 包内的模块
|
||||
if len(module_parts) == 1:
|
||||
# 直接子模块
|
||||
module_name = f"{package_name}.{module_parts[0]}"
|
||||
else:
|
||||
# 子包内的模块
|
||||
module_name = f"{package_name}.{'.'.join(module_parts)}"
|
||||
|
||||
self.add_module(
|
||||
module_name,
|
||||
py_file.read_text(encoding="utf-8"),
|
||||
is_package=False,
|
||||
)
|
||||
63
src/module_bank/sqlite_module_loader.py
Normal file
63
src/module_bank/sqlite_module_loader.py
Normal file
@ -0,0 +1,63 @@
|
||||
import sqlite3
|
||||
import importlib
|
||||
import importlib.abc
|
||||
import marshal
|
||||
import types
|
||||
|
||||
|
||||
class SQLiteModuleLoader(importlib.abc.Loader):
|
||||
"""SQLite模块加载器"""
|
||||
|
||||
def __init__(
|
||||
self, db_connection: sqlite3.Connection, module_name: str, db_path: str = None
|
||||
):
|
||||
self.db_connection = db_connection
|
||||
self.module_name = module_name
|
||||
self.db_path = db_path
|
||||
self.db_connection.row_factory = sqlite3.Row
|
||||
|
||||
def create_module(self, spec):
|
||||
"""创建模块对象"""
|
||||
# 获取模块信息
|
||||
cursor = self.db_connection.execute(
|
||||
"SELECT is_package FROM python_modules WHERE module_name = ?",
|
||||
(self.module_name,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row and row["is_package"]:
|
||||
# 创建包模块
|
||||
module = types.ModuleType(self.module_name)
|
||||
module.__path__ = [] # 包需要有__path__属性
|
||||
module.__package__ = self.module_name
|
||||
return module
|
||||
else:
|
||||
# 创建普通模块
|
||||
return None # 使用默认创建方式
|
||||
|
||||
def exec_module(self, module):
|
||||
"""执行模块代码"""
|
||||
cursor = self.db_connection.execute(
|
||||
"SELECT bytecode, source_code, is_package FROM python_modules WHERE module_name = ?",
|
||||
(self.module_name,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
raise ImportError(f"No module named '{self.module_name}' in database")
|
||||
|
||||
# 从字节码加载
|
||||
bytecode = marshal.loads(row["bytecode"])
|
||||
|
||||
# 在模块的命名空间中执行字节码
|
||||
exec(bytecode, module.__dict__)
|
||||
|
||||
# 如果是包,设置__path__属性
|
||||
if row["is_package"]:
|
||||
module.__path__ = []
|
||||
module.__package__ = self.module_name
|
||||
|
||||
# 设置模块的__file__属性为数据库路径
|
||||
module.__file__ = f"sqlite://{self.db_path}/{self.module_name}"
|
||||
|
||||
return module
|
||||
Loading…
x
Reference in New Issue
Block a user