mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 14:50:45 +00:00
133 lines
4.3 KiB
Python
133 lines
4.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
r"""数据目录自动回退:写不进程序目录就转 ProgramData。
|
||
|
||
背景:
|
||
原来的 data_dir() 就是 app_dir()/data —— 数据存在 exe 旁边。
|
||
绿色版没问题,但**做成安装包装到 Program Files 之后**,
|
||
普通用户对那个目录没有写权限,配置会存不下去。
|
||
|
||
用户选的做法:
|
||
"能写 exe 旁边就写,不能就转 ProgramData"
|
||
|
||
这样两种情况都对:
|
||
* 绿色版 / 装在 D 盘 / 便携 U 盘 -> 还是存在旁边,拔走就带走配置
|
||
* 装在 Program Files -> 自动转 C:\ProgramData\ykxmz,
|
||
所有用户共用一份(配对码不会因为换 Windows 账号而丢)
|
||
|
||
顺带加一个 YKXMZ_DATA 环境变量,测试和特殊部署可以强制指定。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import ast
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
CONFIG = (Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows"
|
||
r"\lanremote\config.py"))
|
||
|
||
OLD = '''def app_dir() -> Path:
|
||
"""程序所在目录(兼容 PyInstaller 冻结环境)。"""
|
||
if getattr(sys, "frozen", False):
|
||
return Path(sys.executable).resolve().parent
|
||
return Path(__file__).resolve().parent.parent
|
||
|
||
|
||
def data_dir() -> Path:
|
||
return app_dir() / "data"'''
|
||
|
||
NEW = '''def app_dir() -> Path:
|
||
"""程序所在目录(兼容 PyInstaller 冻结环境)。"""
|
||
if getattr(sys, "frozen", False):
|
||
return Path(sys.executable).resolve().parent
|
||
return Path(__file__).resolve().parent.parent
|
||
|
||
|
||
# 数据目录只算一次,之后缓存 —— 每个命令都去试写一次太浪费
|
||
_DATA_DIR_CACHE: Path | None = None
|
||
|
||
|
||
def _can_write(folder: Path) -> bool:
|
||
"""这个目录能不能写(不存在就先建)。"""
|
||
try:
|
||
folder.mkdir(parents=True, exist_ok=True)
|
||
probe = folder / ".write-test"
|
||
probe.write_text("x", encoding="utf-8")
|
||
probe.unlink()
|
||
return True
|
||
except OSError:
|
||
return False
|
||
|
||
|
||
def data_dir() -> Path:
|
||
"""配置 / 令牌 / 接收文件的存放目录。
|
||
|
||
优先级:
|
||
1. 环境变量 YKXMZ_DATA(测试和特殊部署用)
|
||
2. 程序目录下的 data/ —— 绿色版、便携 U 盘,拔走就带走配置
|
||
3. %PROGRAMDATA%\\ykxmz\\data —— 装到 Program Files 时用这个
|
||
|
||
为什么要有第 3 条:
|
||
安装包默认装到 C:\\Program Files\\ykxmz,
|
||
普通用户对那里**没有写权限**(Windows 的硬规矩,不是 bug)。
|
||
不转走的话,配对码和令牌会存不下去,用户会莫名其妙连不上。
|
||
ProgramData 是所有用户可写的公共位置,换 Windows 账号也不丢配置。
|
||
"""
|
||
global _DATA_DIR_CACHE
|
||
if _DATA_DIR_CACHE is not None:
|
||
return _DATA_DIR_CACHE
|
||
|
||
override = os.environ.get("YKXMZ_DATA")
|
||
if override:
|
||
_DATA_DIR_CACHE = Path(override)
|
||
return _DATA_DIR_CACHE
|
||
|
||
beside = app_dir() / "data"
|
||
if _can_write(beside):
|
||
_DATA_DIR_CACHE = beside
|
||
return _DATA_DIR_CACHE
|
||
|
||
base = os.environ.get("PROGRAMDATA") or r"C:\\ProgramData"
|
||
common = Path(base) / "ykxmz" / "data"
|
||
try:
|
||
common.mkdir(parents=True, exist_ok=True)
|
||
except OSError:
|
||
# 兜底:连 ProgramData 都写不了(极罕见),退回用户目录
|
||
common = (Path(os.environ.get("LOCALAPPDATA")
|
||
or Path.home()) / "ykxmz" / "data")
|
||
common.mkdir(parents=True, exist_ok=True)
|
||
_DATA_DIR_CACHE = common
|
||
return _DATA_DIR_CACHE'''
|
||
|
||
|
||
def main() -> int:
|
||
text = CONFIG.read_text(encoding="utf-8")
|
||
|
||
if "YKXMZ_DATA" in text:
|
||
print(" 已经改过")
|
||
return 0
|
||
if OLD not in text:
|
||
print(" [失败] 锚点没找到")
|
||
return 1
|
||
|
||
text = text.replace(OLD, NEW, 1)
|
||
|
||
# 确认 os 已经 import 了
|
||
if "\nimport os" not in text:
|
||
text = text.replace("import sys\n", "import os\nimport sys\n", 1)
|
||
print(" 补了 import os")
|
||
|
||
CONFIG.write_text(text, encoding="utf-8", newline="\n")
|
||
print(" data_dir 加了自动回退(ProgramData)")
|
||
|
||
try:
|
||
ast.parse(text)
|
||
print(" config.py 语法 OK")
|
||
except SyntaxError as exc:
|
||
print(f" 语法错误: 行 {exc.lineno}: {exc.msg}")
|
||
return 1
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|