Skip to content

refactor: onedir模式构建#39

Open
ShadowLemoon wants to merge 7 commits intomainfrom
refactor/onedir
Open

refactor: onedir模式构建#39
ShadowLemoon wants to merge 7 commits intomainfrom
refactor/onedir

Conversation

@ShadowLemoon
Copy link
Copy Markdown
Collaborator

@ShadowLemoon ShadowLemoon commented Apr 6, 2026

Summary by CodeRabbit

发布说明

  • 优化改进

    • 改善打包与分发结构,运行时依赖统一放置,安装与压缩流程更稳健。
    • 统一资源定位机制,图片与多语言文本在打包/运行时更可靠地被找到。
    • 配置加载增强:增加缓存以减少重复读取并优化文件选择逻辑。
  • 修复

    • 修正配置存在性判断行为,避免运行时错误与异常分支。
  • 文档

    • 更新安装与升级说明,加入覆盖更新与运行时目录的注意事项。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

重构了应用的资源定位与打包流程:新增 get_resource_path 用于在工作目录或 PyInstaller 运行时(sys._MEIPASS)查找资源,更新若干模块改用该函数加载资源;调整 PyInstaller spec 文件以将资源放入 resources/* 并通过 .runtime 目录和 COLLECT 打包;增加 YAML 读取缓存并将 YamlOperator 的存在检查改为属性。

Changes

Cohort / File(s) Summary
PyInstaller Spec / 打包流程
deploy/OneDragon ScriptChainer.spec, deploy/OneDragon ScriptChainer Runner.spec
调整资源目标路径(configresources/configassetsresources/assets);EXE() 不再直接传入 a.binaries/a.datas,设置 exclude_binaries=Truecontents_directory='.runtime',并新增 COLLECT() 步骤整合二进制与数据。
资源路径工具
src/one_dragon/utils/os_utils.py
新增公有函数 get_resource_path(*sub_paths: str) -> str,并在可执行模式下将 get_work_dir() 改为返回 os.getcwd();若资源缺失则尝试从 sys._MEIPASS/resources/... 解析。
资源加载点(i18n、UI、图像)
src/one_dragon/utils/i18_utils.py, src/one_dragon_qt/app/installer.py, src/one_dragon_qt/view/like_interface.py, src/one_dragon_qt/windows/app_window_base.py
将资源路径解析由 get_path_under_work_dir() 替换为 get_resource_path(),影响翻译 .mo 文件、应用图标与界面图像的定位;加载逻辑本身未改动。
配置与缓存
src/one_dragon/base/config/yaml_operator.py, src/one_dragon/base/config/yaml_config.py
移除旧的 _MEIPASS 特殊处理,新增模块级 YAML 缓存 cached_yaml_dataread_cache_or_load(基于 mtime 缓存解析结果);YamlOperator.is_file_exists 改为属性;YamlConfig 构造签名新增 backup_module_nameread_sample_only,并重写 YAML 文件选择/复制逻辑以优先样本/备份或回退到打包资源路径。
脚本运行器适配
src/script_chainer/win_exe/script_runner.py
YamlOperator.is_file_exists 改为属性,调用点从 chain_config.is_file_exists() 调整为 chain_config.is_file_exists(控制流保持)。
CI / 发布工作流
.github/workflows/build-release.yml
在构建后将 EXE 复制到 dist/OneDragon ScriptChainer/,调整 artifact 上传/下载路径以包含该目录,并更新发布说明(关于 .runtime 和不可单独移动 EXE 的提示)。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

诗篇

🐰 我在草丛里搬资源,蹦蹦跳跳改路径,
把 config 与 assets 放进 resources 的小洞,
.runtime 搭好新巢,YAML 缓存守着门,
打包跑起来咚咚响,我叼着胡萝卜去庆祝 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确反映了主要变更内容:将PyInstaller构建模式从单文件模式改为onedir模式(一个目录),这贯穿整个PR的所有改动。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/onedir

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/one_dragon_qt/app/installer.py (1)

28-30: 这里的图标设置已经和父类重复了。

src/one_dragon_qt/windows/app_window_base.py:27-29 已经处理过 app_icon。继续在子类保留这段,会让安装器窗口的初始化逻辑继续双轨维护,后面很容易出现父子类行为不一致。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon_qt/app/installer.py` around lines 28 - 30, The installer
subclass duplicates icon setup already done in the parent (see
app_window_base.py handling of app_icon); remove the duplicated block in
src/one_dragon_qt/app/installer.py that calls os_utils.get_resource_path(...)
and self.setWindowIcon(QIcon(...)) so the child no longer sets app_icon itself
and instead inherits the parent's behavior (leave any other installer init
intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/utils/yolo_config_utils.py`:
- Around line 8-9: get_model_category_dir currently returns a resource
(read-only) path via get_resource_path which is then passed to OnnxModelLoader
as the download/write directory; change the API to separate read vs write paths:
keep a read helper (e.g. get_builtin_model_category_dir using get_resource_path)
for loading bundled models and add/keep a write helper (e.g.
get_user_model_category_dir using get_path_under_work_dir) for
downloads/updates, and update calls in yolo_model_card.py that pass
get_model_category_dir to OnnxModelLoader to use the user/write helper instead
while retaining the built-in/read helper where models are only read.

---

Nitpick comments:
In `@src/one_dragon_qt/app/installer.py`:
- Around line 28-30: The installer subclass duplicates icon setup already done
in the parent (see app_window_base.py handling of app_icon); remove the
duplicated block in src/one_dragon_qt/app/installer.py that calls
os_utils.get_resource_path(...) and self.setWindowIcon(QIcon(...)) so the child
no longer sets app_icon itself and instead inherits the parent's behavior (leave
any other installer init intact).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bc89d64a-7000-461c-a309-21ef58d9a7a2

📥 Commits

Reviewing files that changed from the base of the PR and between 7dfd8e6 and cea4869.

📒 Files selected for processing (7)
  • deploy/OneDragon ScriptChainer.spec
  • src/one_dragon/utils/i18_utils.py
  • src/one_dragon/utils/os_utils.py
  • src/one_dragon/utils/yolo_config_utils.py
  • src/one_dragon_qt/app/installer.py
  • src/one_dragon_qt/view/like_interface.py
  • src/one_dragon_qt/windows/app_window_base.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
.github/workflows/build-release.yml (2)

123-123: 建议在复制 Runner 前增加显式存在性校验。

Line 123 当前逻辑可用,但在路径变化时报错上下文不够直观。建议在复制前用 Test-Path 做前置校验并给出明确错误信息,方便排查构建失败。

可选修改示例
-        Copy-Item "dist/OneDragon ScriptChainer Runner.exe" -Destination "dist/OneDragon ScriptChainer/" -Force
+        $runnerExe = "dist/OneDragon ScriptChainer Runner.exe"
+        $editorDir = "dist/OneDragon ScriptChainer/"
+        if (-not (Test-Path $runnerExe)) {
+          throw "未找到 Runner 可执行文件:$runnerExe"
+        }
+        if (-not (Test-Path $editorDir)) {
+          throw "未找到 Editor 目录:$editorDir"
+        }
+        Copy-Item $runnerExe -Destination $editorDir -Force
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-release.yml at line 123, 在执行 Copy-Item
"dist/OneDragon ScriptChainer Runner.exe" -Destination "dist/OneDragon
ScriptChainer/" -Force 之前增加显式文件存在性校验:使用 Test-Path 检查 "dist/OneDragon
ScriptChainer Runner.exe" 是否存在,若不存在则输出明确的错误信息(包含被查找的路径)并终止流程(例如用
Write-Error/throw 并返回非零退出码),这样在路径变更或构建产物缺失时能快速定位问题;保留原有 Copy-Item 调用用于实际复制。

130-145: 建议统一为所有上传步骤开启缺失文件即失败。

Line 145 的 Upload Dist 已设置 if-no-files-found: error,但 Upload Editor/Runner(Line 125-138)未设置。建议统一开启,避免产物丢失时“上传成功但内容为空/缺失”。

可选修改示例
     - name: Upload Editor
       if: ${{ env.CREATE_RELEASE == 'false' }}
       uses: actions/upload-artifact@v4
       with:
         name: Editor
         path: deploy/dist/OneDragon ScriptChainer/
+        if-no-files-found: error

     - name: Upload Runner
       if: ${{ env.CREATE_RELEASE == 'false' }}
       uses: actions/upload-artifact@v4
       with:
         name: Runner
         path: deploy/dist/OneDragon ScriptChainer Runner.exe
+        if-no-files-found: error
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-release.yml around lines 130 - 145, Add the missing
if-no-files-found: error to all upload-artifact steps so uploads fail when
artifacts are missing: update the "Upload Runner" step (and any other upload
steps like "Upload Editor") to include with: if-no-files-found: error alongside
their name/path entries, matching the existing "Upload Dist" behavior; ensure
the steps using uses: actions/upload-artifact@v4 include that key to prevent
silent empty uploads.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/build-release.yml:
- Line 123: 在执行 Copy-Item "dist/OneDragon ScriptChainer Runner.exe" -Destination
"dist/OneDragon ScriptChainer/" -Force 之前增加显式文件存在性校验:使用 Test-Path 检查
"dist/OneDragon ScriptChainer Runner.exe" 是否存在,若不存在则输出明确的错误信息(包含被查找的路径)并终止流程(例如用
Write-Error/throw 并返回非零退出码),这样在路径变更或构建产物缺失时能快速定位问题;保留原有 Copy-Item 调用用于实际复制。
- Around line 130-145: Add the missing if-no-files-found: error to all
upload-artifact steps so uploads fail when artifacts are missing: update the
"Upload Runner" step (and any other upload steps like "Upload Editor") to
include with: if-no-files-found: error alongside their name/path entries,
matching the existing "Upload Dist" behavior; ensure the steps using uses:
actions/upload-artifact@v4 include that key to prevent silent empty uploads.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5576cac-f38a-4e4d-a537-da9727f44e83

📥 Commits

Reviewing files that changed from the base of the PR and between 56399d1 and b21a378.

📒 Files selected for processing (1)
  • .github/workflows/build-release.yml

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/one_dragon/utils/os_utils.py (1)

34-46: 资源路径解析逻辑清晰,实现正确。

函数正确处理了开发模式和 PyInstaller 打包模式下的资源查找优先级。有一个小问题:

文档字符串中使用了全角逗号 ,建议改为半角逗号 , 以保持一致性。

📝 建议修复
 def get_resource_path(*sub_paths: str) -> str:
     """获取资源文件路径。
 
-    优先查找工作目录下的路径,不存在时回退到 PyInstaller _MEIPASS。
+    优先查找工作目录下的路径,不存在时回退到 PyInstaller _MEIPASS。
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/os_utils.py` around lines 34 - 46, The docstring in
get_resource_path uses a full-width comma ',' — update the docstring to replace
the full-width comma with a standard ASCII comma ',' so punctuation is
consistent (e.g., change "优先查找工作目录下的路径,不存在时回退到 PyInstaller _MEIPASS。" to use a
half-width comma).
src/one_dragon/base/config/yaml_operator.py (1)

9-21: YAML 缓存机制实现合理。

基于文件 mtime 的缓存失效策略是正确的做法。需要注意:

  1. 如果文件在 os.path.exists() 检查和 os.path.getmtime() 调用之间被删除,会抛出 FileNotFoundError。当前设计依赖调用方(__read_from_file)在调用前检查文件存在性,但存在 TOCTOU(检查时间到使用时间)竞态条件。

  2. 模块级缓存 cached_yaml_data 是全局状态,在多线程环境下可能存在并发访问问题。如果应用是单线程的,这不是问题。

💡 可选:增加防御性处理
 def read_cache_or_load(file_path: str):
     cached = cached_yaml_data.get(file_path)
-    last_modify = os.path.getmtime(file_path)
+    try:
+        last_modify = os.path.getmtime(file_path)
+    except OSError:
+        return None
     if cached is not None and cached[0] == last_modify:
         return cached[1]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/config/yaml_operator.py` around lines 9 - 21,
read_cache_or_load currently reads os.path.getmtime and accesses module-level
cached_yaml_data without defenses against the TOCTOU FileNotFoundError and
concurrent access; wrap the getmtime/open/cache-read/write sequence in a
try/except that catches FileNotFoundError (and optionally OSError) and returns
None (or propagates a clear error) to avoid crashing if the file is removed
between exists/check and use, and add a thread lock (e.g., cached_yaml_lock =
threading.Lock()) to serialize accesses to cached_yaml_data so reads and writes
around cached_yaml_data[file_path] and cached_yaml_data.get(file_path) are done
under the lock; update references to cached_yaml_data and read_cache_or_load
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/one_dragon/base/config/yaml_operator.py`:
- Around line 9-21: read_cache_or_load currently reads os.path.getmtime and
accesses module-level cached_yaml_data without defenses against the TOCTOU
FileNotFoundError and concurrent access; wrap the getmtime/open/cache-read/write
sequence in a try/except that catches FileNotFoundError (and optionally OSError)
and returns None (or propagates a clear error) to avoid crashing if the file is
removed between exists/check and use, and add a thread lock (e.g.,
cached_yaml_lock = threading.Lock()) to serialize accesses to cached_yaml_data
so reads and writes around cached_yaml_data[file_path] and
cached_yaml_data.get(file_path) are done under the lock; update references to
cached_yaml_data and read_cache_or_load accordingly.

In `@src/one_dragon/utils/os_utils.py`:
- Around line 34-46: The docstring in get_resource_path uses a full-width comma
',' — update the docstring to replace the full-width comma with a standard ASCII
comma ',' so punctuation is consistent (e.g., change "优先查找工作目录下的路径,不存在时回退到
PyInstaller _MEIPASS。" to use a half-width comma).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0c6d9a61-92b9-48f4-bb7c-945005a4a8ba

📥 Commits

Reviewing files that changed from the base of the PR and between b21a378 and 9543097.

📒 Files selected for processing (6)
  • deploy/OneDragon ScriptChainer Runner.spec
  • deploy/OneDragon ScriptChainer.spec
  • src/one_dragon/base/config/yaml_operator.py
  • src/one_dragon/utils/i18_utils.py
  • src/one_dragon/utils/os_utils.py
  • src/script_chainer/win_exe/script_runner.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/one_dragon/utils/i18_utils.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/one_dragon/base/config/yaml_operator.py (1)

8-8: 全局 YAML 缓存缺少淘汰策略,长期运行可能持续占用内存。

建议给 cached_yaml_data 增加上限(LRU/FIFO 均可),避免路径数量增长导致常驻内存不可控。

Also applies to: 10-20

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/config/yaml_operator.py` at line 8, cached_yaml_data is
an unbounded global dict and needs a size limit to prevent unbounded memory
growth; introduce a configurable max (e.g., YAML_CACHE_MAX) and use an eviction
policy (LRU recommended) when inserting into cached_yaml_data — implement this
by replacing the plain dict with an ordered mapping (collections.OrderedDict or
a small LRU wrapper) or by adding logic in the function that populates
cached_yaml_data (look for functions that set cached_yaml_data entries) to pop
the least-recently-used/oldest key once len(cached_yaml_data) > YAML_CACHE_MAX;
ensure access updates recency if using LRU and add a default max constant and a
short comment explaining the policy.
src/one_dragon/utils/os_utils.py (1)

142-151: get_monday_dt 的文档与变量名建议同步到“周一”语义。

当前返回说明与局部变量名仍是“星期天”,易误导维护者。

建议修复
 def get_monday_dt(dt: str) -> str:
@@
-    :return: 星期天日期 yyyyMMdd 格式
+    :return: 星期一日期 yyyyMMdd 格式
@@
-    sunday_date = date + datetime.timedelta(days=-weekday)
-    return sunday_date.strftime("%Y%m%d")
+    monday_date = date + datetime.timedelta(days=-weekday)
+    return monday_date.strftime("%Y%m%d")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/os_utils.py` around lines 142 - 151, The get_monday_dt
function's docstring and local variable names incorrectly reference "Sunday"
while the logic returns Monday; rename the misleading symbol(s) and update the
docstring to reflect "Monday" semantics (e.g., change sunday_date to monday_date
and update the return description and parameter doc to say 星期一/周一), and ensure
any inline comment about weekday mapping consistently states 0 = Monday; keep
the existing computation (date + datetime.timedelta(days=-weekday)) which
correctly yields Monday.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/base/config/yaml_config.py`:
- Line 42: The docstring line """是否只读取sample文件(即使.yml文件存在也只读sample)""" contains
full-width parentheses which trigger Ruff; update that string to use
ASCII/half-width parentheses like """是否只读取sample文件(即使.yml文件存在也只读sample)"""
(locate the exact docstring in yaml_config.py), save and run ruff/linters to
confirm the warning is resolved.
- Around line 82-84: 当前实现:在判断 self._copy_from_sample 为 True 后执行
shutil.copyfile(sample_yml_path, yml_path) 但仍返回
sample_yml_path,导致后续写入错误。修改该方法使其在执行复制后返回目标路径 yml_path(而非 sample_yml_path);即保留
shutil.copyfile(sample_yml_path, yml_path) 调用不变,但在返回值处根据 self._copy_from_sample
或复制结果返回 yml_path,确保后续保存落在正确的 <module>.yml
文件(引用符号:self._copy_from_sample、sample_yml_path、yml_path)。

In `@src/one_dragon/base/config/yaml_operator.py`:
- Around line 10-20: The cache currently returns the same mutable object to
callers and doesn't enforce the YAML root to be a dict, causing shared-mutation
bugs and type-contract violations; in read_cache_or_load you should (1) validate
the result of yaml_utils.safe_load(file) is a dict (raise a clear TypeError or
convert to an empty dict if your contract requires) and (2) when returning
cached_yaml_data[file_path][1] return a defensive copy (e.g., a deep copy) so
callers do not share the same dict instance; update any callers that assume
YamlOperator.data is always a dict to rely on the validated/defensively-copied
value.

In `@src/one_dragon/utils/os_utils.py`:
- Around line 64-66: get_work_dir() currently returns os.getcwd() when
run_in_exe() is true which yields an unstable working directory; instead detect
the frozen/exe context and return the directory of the running executable/bundle
so relative paths remain stable. Modify get_work_dir()/run_in_exe() branch to
use the executable location (e.g., os.path.dirname(sys.executable) or the frozen
bundle path such as getattr(sys, "frozen", False) and sys._MEIPASS when
applicable) rather than os.getcwd(); ensure callers like
src/one_dragon/utils/cmd_utils.py that rely on get_work_dir() (e.g.,
subprocess.Popen default cwd) receive the executable directory.

---

Nitpick comments:
In `@src/one_dragon/base/config/yaml_operator.py`:
- Line 8: cached_yaml_data is an unbounded global dict and needs a size limit to
prevent unbounded memory growth; introduce a configurable max (e.g.,
YAML_CACHE_MAX) and use an eviction policy (LRU recommended) when inserting into
cached_yaml_data — implement this by replacing the plain dict with an ordered
mapping (collections.OrderedDict or a small LRU wrapper) or by adding logic in
the function that populates cached_yaml_data (look for functions that set
cached_yaml_data entries) to pop the least-recently-used/oldest key once
len(cached_yaml_data) > YAML_CACHE_MAX; ensure access updates recency if using
LRU and add a default max constant and a short comment explaining the policy.

In `@src/one_dragon/utils/os_utils.py`:
- Around line 142-151: The get_monday_dt function's docstring and local variable
names incorrectly reference "Sunday" while the logic returns Monday; rename the
misleading symbol(s) and update the docstring to reflect "Monday" semantics
(e.g., change sunday_date to monday_date and update the return description and
parameter doc to say 星期一/周一), and ensure any inline comment about weekday
mapping consistently states 0 = Monday; keep the existing computation (date +
datetime.timedelta(days=-weekday)) which correctly yields Monday.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dcf8c15f-4d0f-4761-b9b0-08602f6f84cb

📥 Commits

Reviewing files that changed from the base of the PR and between 9543097 and 3d4440e.

📒 Files selected for processing (3)
  • src/one_dragon/base/config/yaml_config.py
  • src/one_dragon/base/config/yaml_operator.py
  • src/one_dragon/utils/os_utils.py

"""配置文件不存在时 是否从sample文件中读取"""

self._read_sample_only: bool = read_sample_only
"""是否只读取sample文件(即使.yml文件存在也只读sample)"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

注释中的全角括号会持续触发 Ruff 告警,建议替换为半角。

这是低风险问题,但会增加静态检查噪音。

🧰 Tools
🪛 Ruff (0.15.9)

[warning] 42-42: String contains ambiguous (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?

(RUF001)


[warning] 42-42: String contains ambiguous (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?

(RUF001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/config/yaml_config.py` at line 42, The docstring line
"""是否只读取sample文件(即使.yml文件存在也只读sample)""" contains full-width parentheses which
trigger Ruff; update that string to use ASCII/half-width parentheses like
"""是否只读取sample文件(即使.yml文件存在也只读sample)""" (locate the exact docstring in
yaml_config.py), save and run ruff/linters to confirm the warning is resolved.

Comment on lines +82 to +84
if self._copy_from_sample:
shutil.copyfile(sample_yml_path, yml_path)
return sample_yml_path
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

从 sample 复制后仍返回 sample 路径,会导致写入目标错误。

copy_from_sample=True 时已复制到 <module>.yml,但返回 sample_yml_path 会让后续保存仍落到 sample 文件。

建议修复
         if self._sample and os.path.exists(sample_yml_path):
             if self._copy_from_sample:
                 shutil.copyfile(sample_yml_path, yml_path)
-            return sample_yml_path
+                return yml_path
+            return sample_yml_path
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self._copy_from_sample:
shutil.copyfile(sample_yml_path, yml_path)
return sample_yml_path
if self._copy_from_sample:
shutil.copyfile(sample_yml_path, yml_path)
return yml_path
return sample_yml_path
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/config/yaml_config.py` around lines 82 - 84, 当前实现:在判断
self._copy_from_sample 为 True 后执行 shutil.copyfile(sample_yml_path, yml_path)
但仍返回 sample_yml_path,导致后续写入错误。修改该方法使其在执行复制后返回目标路径 yml_path(而非
sample_yml_path);即保留 shutil.copyfile(sample_yml_path, yml_path) 调用不变,但在返回值处根据
self._copy_from_sample 或复制结果返回 yml_path,确保后续保存落在正确的 <module>.yml
文件(引用符号:self._copy_from_sample、sample_yml_path、yml_path)。

Comment on lines +10 to +20
def read_cache_or_load(file_path: str):
cached = cached_yaml_data.get(file_path)
last_modify = os.path.getmtime(file_path)
if cached is not None and cached[0] == last_modify:
return cached[1]

with open(file_path, 'r', encoding='utf-8') as file:
log.debug(f"加载yaml: {file_path}")
data = yaml_utils.safe_load(file)
cached_yaml_data[file_path] = (last_modify, data)
return data
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

缓存返回共享可变对象且未约束 YAML 根类型,存在串扰与运行时风险。

当前命中缓存会把同一个 dict 引用分发给多个实例;同时未校验 safe_load 的返回类型,遇到 list/scalar 配置时会破坏 YamlOperator.data 的字典契约。

可参考的修正 diff
 import os
+import copy
 
 import yaml
@@
-cached_yaml_data: dict[str, tuple[float, dict]] = {}
+cached_yaml_data: dict[str, tuple[float, dict]] = {}
@@
-def read_cache_or_load(file_path: str):
+def read_cache_or_load(file_path: str) -> dict:
     cached = cached_yaml_data.get(file_path)
     last_modify = os.path.getmtime(file_path)
     if cached is not None and cached[0] == last_modify:
-        return cached[1]
+        return copy.deepcopy(cached[1])
@@
     with open(file_path, 'r', encoding='utf-8') as file:
         log.debug(f"加载yaml: {file_path}")
-        data = yaml_utils.safe_load(file)
+        raw = yaml_utils.safe_load(file)
+        data = raw if isinstance(raw, dict) else {}
         cached_yaml_data[file_path] = (last_modify, data)
-        return data
+        return copy.deepcopy(data)

Also applies to: 50-50

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/config/yaml_operator.py` around lines 10 - 20, The cache
currently returns the same mutable object to callers and doesn't enforce the
YAML root to be a dict, causing shared-mutation bugs and type-contract
violations; in read_cache_or_load you should (1) validate the result of
yaml_utils.safe_load(file) is a dict (raise a clear TypeError or convert to an
empty dict if your contract requires) and (2) when returning
cached_yaml_data[file_path][1] return a defensive copy (e.g., a deep copy) so
callers do not share the same dict instance; update any callers that assume
YamlOperator.data is always a dict to rely on the validated/defensively-copied
value.

Comment on lines 64 to 66
if run_in_exe():
return os.path.dirname(sys.executable)
return os.getcwd()
dir_path: str = os.path.abspath(__file__)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

exe 模式下使用 os.getcwd() 会引入不稳定工作目录。

这会影响依赖 get_work_dir() 的调用方(例如 src/one_dragon/utils/cmd_utils.pysubprocess.Popen 的默认 cwd),导致从不同启动位置运行时行为不一致,出现相对路径失效风险。

建议修复
 def get_work_dir() -> str:
@@
     if run_in_exe():
-        return os.getcwd()
+        return os.path.dirname(sys.executable)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/os_utils.py` around lines 64 - 66, get_work_dir()
currently returns os.getcwd() when run_in_exe() is true which yields an unstable
working directory; instead detect the frozen/exe context and return the
directory of the running executable/bundle so relative paths remain stable.
Modify get_work_dir()/run_in_exe() branch to use the executable location (e.g.,
os.path.dirname(sys.executable) or the frozen bundle path such as getattr(sys,
"frozen", False) and sys._MEIPASS when applicable) rather than os.getcwd();
ensure callers like src/one_dragon/utils/cmd_utils.py that rely on
get_work_dir() (e.g., subprocess.Popen default cwd) receive the executable
directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant