Skip to content

Commit 283c7b4

Browse files
authored
Merge pull request #973 from iorisa/fixbug/gbk
fixbug: gbk UnicodeEncodeError
2 parents fe9dae1 + 9d325d4 commit 283c7b4

File tree

10 files changed

+43
-42
lines changed

10 files changed

+43
-42
lines changed

metagpt/learn/skill_loader.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
from pathlib import Path
1010
from typing import Dict, List, Optional
1111

12-
import aiofiles
1312
import yaml
1413
from pydantic import BaseModel, Field
1514

1615
from metagpt.context import Context
16+
from metagpt.utils.common import aread
1717

1818

1919
class Example(BaseModel):
@@ -68,8 +68,7 @@ class SkillsDeclaration(BaseModel):
6868
async def load(skill_yaml_file_name: Path = None) -> "SkillsDeclaration":
6969
if not skill_yaml_file_name:
7070
skill_yaml_file_name = Path(__file__).parent.parent.parent / "docs/.well-known/skills.yaml"
71-
async with aiofiles.open(str(skill_yaml_file_name), mode="r") as reader:
72-
data = await reader.read(-1)
71+
data = await aread(filename=skill_yaml_file_name)
7372
skill_data = yaml.safe_load(data)
7473
return SkillsDeclaration(**skill_data)
7574

metagpt/utils/common.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from typing import Any, Callable, List, Tuple, Union
3030

3131
import aiofiles
32+
import chardet
3233
import loguru
3334
import requests
3435
from PIL import Image
@@ -587,14 +588,21 @@ async def wrapper(self, *args, **kwargs):
587588

588589

589590
@handle_exception
590-
async def aread(filename: str | Path, encoding=None) -> str:
591+
async def aread(filename: str | Path, encoding="utf-8") -> str:
591592
"""Read file asynchronously."""
592-
async with aiofiles.open(str(filename), mode="r", encoding=encoding) as reader:
593-
content = await reader.read()
593+
try:
594+
async with aiofiles.open(str(filename), mode="r", encoding=encoding) as reader:
595+
content = await reader.read()
596+
except UnicodeDecodeError:
597+
async with aiofiles.open(str(filename), mode="rb") as reader:
598+
raw = await reader.read()
599+
result = chardet.detect(raw)
600+
detected_encoding = result["encoding"]
601+
content = raw.decode(detected_encoding)
594602
return content
595603

596604

597-
async def awrite(filename: str | Path, data: str, encoding=None):
605+
async def awrite(filename: str | Path, data: str, encoding="utf-8"):
598606
"""Write file asynchronously."""
599607
pathname = Path(filename)
600608
pathname.parent.mkdir(parents=True, exist_ok=True)

metagpt/utils/dependency_file.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
from pathlib import Path
1414
from typing import Set
1515

16-
import aiofiles
17-
18-
from metagpt.utils.common import aread
16+
from metagpt.utils.common import aread, awrite
1917
from metagpt.utils.exceptions import handle_exception
2018

2119

@@ -45,8 +43,7 @@ async def load(self):
4543
async def save(self):
4644
"""Save dependencies to the file asynchronously."""
4745
data = json.dumps(self._dependencies)
48-
async with aiofiles.open(str(self._filename), mode="w") as writer:
49-
await writer.write(data)
46+
await awrite(filename=self._filename, data=data)
5047

5148
async def update(self, filename: Path | str, dependencies: Set[Path | str], persist=True):
5249
"""Update dependencies for a file asynchronously.

metagpt/utils/file_repository.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
from pathlib import Path
1515
from typing import Dict, List, Set
1616

17-
import aiofiles
18-
1917
from metagpt.logs import logger
2018
from metagpt.schema import Document
21-
from metagpt.utils.common import aread
19+
from metagpt.utils.common import aread, awrite
2220
from metagpt.utils.json_to_markdown import json_to_markdown
2321

2422

@@ -55,8 +53,7 @@ async def save(self, filename: Path | str, content, dependencies: List[str] = No
5553
pathname = self.workdir / filename
5654
pathname.parent.mkdir(parents=True, exist_ok=True)
5755
content = content if content else "" # avoid `argument must be str, not None` to make it continue
58-
async with aiofiles.open(str(pathname), mode="w") as writer:
59-
await writer.write(content)
56+
await awrite(filename=str(pathname), data=content)
6057
logger.info(f"save to: {str(pathname)}")
6158

6259
if dependencies is not None:

metagpt/utils/mermaid.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99
import os
1010
from pathlib import Path
1111

12-
import aiofiles
13-
1412
from metagpt.config2 import config
1513
from metagpt.logs import logger
16-
from metagpt.utils.common import check_cmd_exists
14+
from metagpt.utils.common import awrite, check_cmd_exists
1715

1816

1917
async def mermaid_to_file(engine, mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int:
@@ -30,9 +28,7 @@ async def mermaid_to_file(engine, mermaid_code, output_file_without_suffix, widt
3028
if dir_name and not os.path.exists(dir_name):
3129
os.makedirs(dir_name)
3230
tmp = Path(f"{output_file_without_suffix}.mmd")
33-
async with aiofiles.open(tmp, "w", encoding="utf-8") as f:
34-
await f.write(mermaid_code)
35-
# tmp.write_text(mermaid_code, encoding="utf-8")
31+
await awrite(filename=tmp, data=mermaid_code)
3632

3733
if engine == "nodejs":
3834
if check_cmd_exists(config.mermaid.path) != 0:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def run(self):
5757

5858
setup(
5959
name="metagpt",
60-
version="0.7.5",
60+
version="0.7.6",
6161
description="The Multi-Agent Framework",
6262
long_description=long_description,
6363
long_description_content_type="text/markdown",

tests/metagpt/roles/test_tutorial_assistant.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
@File : test_tutorial_assistant.py
77
"""
88

9-
import aiofiles
109
import pytest
1110

1211
from metagpt.const import TUTORIAL_PATH
1312
from metagpt.roles.tutorial_assistant import TutorialAssistant
13+
from metagpt.utils.common import aread
1414

1515

1616
@pytest.mark.asyncio
@@ -20,9 +20,8 @@ async def test_tutorial_assistant(language: str, topic: str, context):
2020
msg = await role.run(topic)
2121
assert TUTORIAL_PATH.exists()
2222
filename = msg.content
23-
async with aiofiles.open(filename, mode="r", encoding="utf-8") as reader:
24-
content = await reader.read()
25-
assert "pip" in content
23+
content = await aread(filename=filename)
24+
assert "pip" in content
2625

2726

2827
if __name__ == "__main__":

tests/metagpt/utils/test_common.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from pathlib import Path
1414
from typing import Any, Set
1515

16-
import aiofiles
1716
import pytest
1817
from pydantic import BaseModel
1918

@@ -125,9 +124,7 @@ class Input(BaseModel):
125124
async def test_parse_data_exception(self, filename, want):
126125
pathname = Path(__file__).parent.parent.parent / "data/output_parser" / filename
127126
assert pathname.exists()
128-
async with aiofiles.open(str(pathname), mode="r") as reader:
129-
data = await reader.read()
130-
127+
data = await aread(filename=pathname)
131128
result = OutputParser.parse_data(data=data)
132129
assert want in result
133130

@@ -198,12 +195,25 @@ async def test_read_file_block(self):
198195

199196
@pytest.mark.asyncio
200197
async def test_read_write(self):
201-
pathname = Path(__file__).parent / uuid.uuid4().hex / "test.tmp"
198+
pathname = Path(__file__).parent / f"../../../workspace/unittest/{uuid.uuid4().hex}" / "test.tmp"
202199
await awrite(pathname, "ABC")
203200
data = await aread(pathname)
204201
assert data == "ABC"
205202
pathname.unlink(missing_ok=True)
206203

204+
@pytest.mark.asyncio
205+
async def test_read_write_error_charset(self):
206+
pathname = Path(__file__).parent / f"../../../workspace/unittest/{uuid.uuid4().hex}" / "test.txt"
207+
content = "中国abc123\u27f6"
208+
await awrite(filename=pathname, data=content)
209+
data = await aread(filename=pathname)
210+
assert data == content
211+
212+
content = "GB18030 是中国国家标准局发布的新一代中文字符集标准,是 GBK 的升级版,支持更广泛的字符范围。"
213+
await awrite(filename=pathname, data=content, encoding="gb2312")
214+
data = await aread(filename=pathname, encoding="utf-8")
215+
assert data == content
216+
207217

208218
if __name__ == "__main__":
209219
pytest.main([__file__, "-s"])

tests/metagpt/utils/test_git_repository.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
import shutil
1111
from pathlib import Path
1212

13-
import aiofiles
1413
import pytest
1514

15+
from metagpt.utils.common import awrite
1616
from metagpt.utils.git_repository import GitRepository
1717

1818

1919
async def mock_file(filename, content=""):
20-
async with aiofiles.open(str(filename), mode="w") as file:
21-
await file.write(content)
20+
await awrite(filename=filename, data=content)
2221

2322

2423
async def mock_repo(local_path) -> (GitRepository, Path):

tests/metagpt/utils/test_s3.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from pathlib import Path
1010

1111
import aioboto3
12-
import aiofiles
1312
import pytest
1413

1514
from metagpt.config2 import Config
@@ -37,16 +36,15 @@ async def test_s3(mocker):
3736
conn = S3(s3)
3837
object_name = "unittest.bak"
3938
await conn.upload_file(bucket=s3.bucket, local_path=__file__, object_name=object_name)
40-
pathname = (Path(__file__).parent / uuid.uuid4().hex).with_suffix(".bak")
39+
pathname = (Path(__file__).parent / "../../../workspace/unittest" / uuid.uuid4().hex).with_suffix(".bak")
4140
pathname.unlink(missing_ok=True)
4241
await conn.download_file(bucket=s3.bucket, object_name=object_name, local_path=str(pathname))
4342
assert pathname.exists()
4443
url = await conn.get_object_url(bucket=s3.bucket, object_name=object_name)
4544
assert url
4645
bin_data = await conn.get_object(bucket=s3.bucket, object_name=object_name)
4746
assert bin_data
48-
async with aiofiles.open(__file__, mode="r", encoding="utf-8") as reader:
49-
data = await reader.read()
47+
data = await aread(filename=__file__)
5048
res = await conn.cache(data, ".bak", "script")
5149
assert "http" in res
5250

@@ -60,8 +58,6 @@ async def test_s3(mocker):
6058
except Exception:
6159
pass
6260

63-
await reader.close()
64-
6561

6662
if __name__ == "__main__":
6763
pytest.main([__file__, "-s"])

0 commit comments

Comments
 (0)