pycryptodome:No module named ‘Crypto’
安装了 pycryptodome 但运行时报错:
ModuleNotFoundError: No module named 'Crypto'
原因:pycryptodome 安装后的导入路径是 Crypto(大写C),和旧版 pycrypto 同名,但两者不兼容。虚拟环境里如果同时装了两个就会冲突。
解决:
pip uninstall pycrypto pycryptodome
pip install pycryptodome
导入方式不变:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
如果想用 Cryptodome 命名空间(与 pycrypto 完全隔离),改装 pycryptodomex:
pip install pycryptodomex
from Cryptodome.Cipher import AES
虚拟环境里确认安装:
pip list | grep -i crypto
# pycryptodome 3.20.0
shutil 目录同步加 tqdm 进度条
shutil.copytree 不支持进度回调,需要手动遍历:
import shutil
from pathlib import Path
from tqdm import tqdm
def sync_dir(src: Path, dst: Path):
files = [f for f in src.rglob("*") if f.is_file()]
dst.mkdir(parents=True, exist_ok=True)
for src_file in tqdm(files, desc="复制中", unit="file"):
rel = src_file.relative_to(src)
dst_file = dst / rel
dst_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_file, dst_file)
shutil.copy2 会保留原文件的修改时间和权限位。
覆盖前备份,保留最近 N 个
from datetime import datetime
def backup_and_replace(src: Path, dst: Path, keep=5):
if dst.exists():
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
backup = dst.parent / f"{dst.name}_bak_{ts}"
dst.rename(backup)
# 清理旧备份,只保留最近 keep 个
baks = sorted(dst.parent.glob(f"{dst.name}_bak_*"))
for old in baks[:-keep]:
shutil.rmtree(old)
sync_dir(src, dst)
PyInstaller 打包后路径检测
-F 打包成单文件 exe 后,__file__ 不再可用,需要用 sys.executable:
import sys
from pathlib import Path
if getattr(sys, "frozen", False):
# 打包环境:exe 所在目录
BASE_DIR = Path(sys.executable).parent
else:
# 开发环境:脚本所在目录
BASE_DIR = Path(__file__).parent
CONFIG_PATH = BASE_DIR / "config.json"
sys.frozen 在 PyInstaller 打包环境下为 True,普通 Python 解释器下不存在(getattr 返回 False)。
打包命令:
pyinstaller -F -n mytool --noconsole main.py
-F:单文件-n mytool:输出名称--noconsole:不弹出控制台窗口(GUI 工具用)
完整示例:插件目录同步工具
import sys
import json
import shutil
from pathlib import Path
from datetime import datetime
from tqdm import tqdm
BASE_DIR = Path(sys.executable).parent if getattr(sys, "frozen", False) else Path(__file__).parent
CONFIG = json.loads((BASE_DIR / "config.json").read_text(encoding="utf-8"))
SRC = Path(CONFIG["src"])
DST = Path(CONFIG["dst"])
def backup(dst: Path, keep=5):
if not dst.exists():
return
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
dst.rename(dst.parent / f"{dst.name}_bak_{ts}")
for old in sorted(dst.parent.glob(f"{dst.name}_bak_*"))[:-keep]:
shutil.rmtree(old)
def copy_with_progress(src: Path, dst: Path):
files = [f for f in src.rglob("*") if f.is_file()]
dst.mkdir(parents=True, exist_ok=True)
for f in tqdm(files, unit="file"):
rel = f.relative_to(src)
out = dst / rel
out.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(f, out)
backup(DST)
copy_with_progress(SRC, DST)
print("同步完成")
config.json:
{
"src": "C:/Users/用户名/AppData/Roaming/adspower_global/cwd_global/chrome/Default",
"dst": "D:/backup/chrome_profile"
}