Skip to content

fix: accept UTF-8 BOM in plugin schemas#9223

Open
lingyun14beta wants to merge 2 commits into
AstrBotDevs:masterfrom
lingyun14beta:BOM
Open

fix: accept UTF-8 BOM in plugin schemas#9223
lingyun14beta wants to merge 2 commits into
AstrBotDevs:masterfrom
lingyun14beta:BOM

Conversation

@lingyun14beta

@lingyun14beta lingyun14beta commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

fix #9222

Modifications / 改动点

  • 修改 astrbot/core/star/star_manager.py

    • 新增 _load_plugin_config_schema(),统一处理插件 _conf_schema.json 的读取与解析。
    • 使用 utf-8-sig 读取插件配置 schema,同时兼容 UTF-8 无 BOM 和 UTF-8 BOM。
    • 分别处理编码错误与 JSON 语法错误,并在 JSON 错误信息中提供行号和列号。
    • 使用 utf-8-sig 读取 .astrbot-plugin/i18n/*.json,避免带 BOM 的插件翻译文件加载失败。
  • 修改 tests/test_plugin_manager.py

    • 添加带 UTF-8 BOM 的插件配置 schema 测试。
    • 添加无 BOM 的普通 UTF-8 schema 测试。
    • 添加无效 JSON schema 的错误处理测试。
    • 扩展插件 i18n 测试,同时覆盖带 BOM 和无 BOM 的 locale JSON 文件。
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Handle plugin config schemas and i18n locale files encoded with UTF-8 BOM while improving error reporting.

Bug Fixes:

  • Allow plugin config schema files with UTF-8 BOM to be parsed correctly.
  • Ensure plugin i18n locale JSON files with UTF-8 BOM are loaded without errors.
  • Report invalid plugin config schema JSON with clear errors including line and column information.

Enhancements:

  • Introduce a dedicated helper to load plugin config schemas with consistent UTF-8 handling.
  • Extend plugin manager tests to cover UTF-8 BOM and non-BOM schemas, invalid JSON handling, and BOM i18n files.

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jul 12, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for UTF-8 BOM when loading plugin internationalization (i18n) files and configuration schemas. It introduces a helper method _load_plugin_config_schema with robust error handling for encoding and JSON decoding issues, accompanied by new unit tests. The feedback suggests enhancing the schema loader to explicitly validate that the parsed JSON root is a dictionary (JSON object) and adding a corresponding test case to ensure robust error reporting.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +578 to +592
@staticmethod
def _load_plugin_config_schema(schema_path: str) -> dict:
"""Load a plugin config schema, accepting an optional UTF-8 BOM."""
try:
with open(schema_path, encoding="utf-8-sig") as f:
return json.load(f)
except UnicodeDecodeError as exc:
raise ValueError(
f"插件配置 schema 必须使用 UTF-8 编码: {schema_path}"
) from exc
except json.JSONDecodeError as exc:
raise ValueError(
f"插件配置 schema 不是有效的 JSON: {schema_path} "
f"(line {exc.lineno}, column {exc.colno})"
) from exc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

为了提高代码的健壮性并提供更清晰的错误信息,建议在 _load_plugin_config_schema 中验证解析后的 JSON 根节点是否为字典(JSON 对象)。如果插件开发者提供了非字典格式的 schema(例如 JSON 数组或字符串),在后续解析时会抛出不直观的 AttributeError。提前进行类型检查并抛出明确的 ValueError 可以显著提升开发者的调试体验。

Suggested change
@staticmethod
def _load_plugin_config_schema(schema_path: str) -> dict:
"""Load a plugin config schema, accepting an optional UTF-8 BOM."""
try:
with open(schema_path, encoding="utf-8-sig") as f:
return json.load(f)
except UnicodeDecodeError as exc:
raise ValueError(
f"插件配置 schema 必须使用 UTF-8 编码: {schema_path}"
) from exc
except json.JSONDecodeError as exc:
raise ValueError(
f"插件配置 schema 不是有效的 JSON: {schema_path} "
f"(line {exc.lineno}, column {exc.colno})"
) from exc
@staticmethod
def _load_plugin_config_schema(schema_path: str) -> dict:
"""Load a plugin config schema, accepting an optional UTF-8 BOM."""
try:
with open(schema_path, encoding="utf-8-sig") as f:
schema = json.load(f)
if not isinstance(schema, dict):
raise ValueError(
f"插件配置 schema 根节点必须是一个 JSON 对象: {schema_path}"
)
return schema
except UnicodeDecodeError as exc:
raise ValueError(
f"插件配置 schema 必须使用 UTF-8 编码: {schema_path}"
) from exc
except json.JSONDecodeError as exc:
raise ValueError(
f"插件配置 schema 不是有效的 JSON: {schema_path} "
f"(line {exc.lineno}, column {exc.colno})"
) from exc

Comment on lines +41 to +47
def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_text("{invalid", encoding="utf-8")

with pytest.raises(ValueError, match="不是有效的 JSON"):
PluginManager._load_plugin_config_schema(str(schema_path))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

配合 _load_plugin_config_schema 中新增的 JSON 根节点类型校验,建议在此处添加对应的单元测试,以确保当 schema 不是有效的 JSON 对象(例如是一个 JSON 数组)时,能够正确抛出预期的 ValueError 并包含清晰的错误提示。

Suggested change
def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_text("{invalid", encoding="utf-8")
with pytest.raises(ValueError, match="不是有效的 JSON"):
PluginManager._load_plugin_config_schema(str(schema_path))
def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_text("{invalid", encoding="utf-8")
with pytest.raises(ValueError, match="不是有效的 JSON"):
PluginManager._load_plugin_config_schema(str(schema_path))
def test_load_plugin_config_schema_reports_non_dict(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_text('["not", "a", "dict"]', encoding="utf-8")
with pytest.raises(ValueError, match="根节点必须是一个 JSON 对象"):
PluginManager._load_plugin_config_schema(str(schema_path))

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In _load_plugin_config_schema, consider accepting os.PathLike/Path objects instead of only str (and using Path(schema_path).open(...)) to align with the rest of the codebase’s path handling and make the helper more flexible.
  • When wrapping json.JSONDecodeError in _load_plugin_config_schema, you might include exc.msg in the error text so callers get the original parsing reason in addition to the line/column information.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_load_plugin_config_schema`, consider accepting `os.PathLike`/`Path` objects instead of only `str` (and using `Path(schema_path).open(...)`) to align with the rest of the codebase’s path handling and make the helper more flexible.
- When wrapping `json.JSONDecodeError` in `_load_plugin_config_schema`, you might include `exc.msg` in the error text so callers get the original parsing reason in addition to the line/column information.

## Individual Comments

### Comment 1
<location path="tests/test_plugin_manager.py" line_range="41-45" />
<code_context>
+    }
+
+
+def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path):
+    schema_path = tmp_path / "_conf_schema.json"
+    schema_path.write_text("{invalid", encoding="utf-8")
+
+    with pytest.raises(ValueError, match="不是有效的 JSON"):
+        PluginManager._load_plugin_config_schema(str(schema_path))
+
</code_context>
<issue_to_address>
**suggestion (testing):** Extend the invalid JSON schema test to assert that line and column information are included in the error message.

To make this behavior part of the contract, consider capturing the `ValueError` and asserting the presence of line/column info in the message, e.g.:

```python
with pytest.raises(ValueError) as excinfo:
    PluginManager._load_plugin_config_schema(str(schema_path))

msg = str(excinfo.value)
assert "不是有效的 JSON" in msg
assert "line " in msg
assert "column " in msg
```

This will help prevent future refactors from dropping the improved diagnostics.
</issue_to_address>

### Comment 2
<location path="tests/test_plugin_manager.py" line_range="23-28" />
<code_context>
 TEST_PLUGIN_DIR = "helloworld"


+def test_load_plugin_config_schema_accepts_utf8_bom(tmp_path: Path):
+    schema_path = tmp_path / "_conf_schema.json"
+    schema_path.write_bytes(b'\xef\xbb\xbf{"type": "object"}')
+
+    assert PluginManager._load_plugin_config_schema(str(schema_path)) == {
+        "type": "object"
+    }
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test for non-UTF-8 schemas to cover the UnicodeDecodeError branch in `_load_plugin_config_schema`.

Current tests cover UTF-8 (with/without BOM) and invalid JSON, but not files encoded with a different charset (e.g. GBK/ISO-8859-1). Since `_load_plugin_config_schema` has a specific `UnicodeDecodeError` branch that raises a `ValueError` about UTF-8, please add a test that writes the schema with a non-UTF-8 encoding and asserts that this `ValueError` (with the expected message) is raised, so that branch is covered.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +41 to +45
def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_text("{invalid", encoding="utf-8")

with pytest.raises(ValueError, match="不是有效的 JSON"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Extend the invalid JSON schema test to assert that line and column information are included in the error message.

To make this behavior part of the contract, consider capturing the ValueError and asserting the presence of line/column info in the message, e.g.:

with pytest.raises(ValueError) as excinfo:
    PluginManager._load_plugin_config_schema(str(schema_path))

msg = str(excinfo.value)
assert "不是有效的 JSON" in msg
assert "line " in msg
assert "column " in msg

This will help prevent future refactors from dropping the improved diagnostics.

Comment on lines +23 to +28
def test_load_plugin_config_schema_accepts_utf8_bom(tmp_path: Path):
schema_path = tmp_path / "_conf_schema.json"
schema_path.write_bytes(b'\xef\xbb\xbf{"type": "object"}')

assert PluginManager._load_plugin_config_schema(str(schema_path)) == {
"type": "object"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add a test for non-UTF-8 schemas to cover the UnicodeDecodeError branch in _load_plugin_config_schema.

Current tests cover UTF-8 (with/without BOM) and invalid JSON, but not files encoded with a different charset (e.g. GBK/ISO-8859-1). Since _load_plugin_config_schema has a specific UnicodeDecodeError branch that raises a ValueError about UTF-8, please add a test that writes the schema with a non-UTF-8 encoding and asserts that this ValueError (with the expected message) is raised, so that branch is covered.

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

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]插件配置 schema 带 UTF-8 BOM 时无法安装

1 participant