diff --git a/.jules/bolt.md b/.jules/bolt.md index 1d2f017a..1625a862 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -63,3 +63,7 @@ ## 2024-07-30 - Avoid chained string replace when checking character sets **Learning:** Using chained `.replace(a, "").replace(b, "")` to check if a string consists entirely of specific characters requires intermediate string allocations for every call. In benchmarks, using `.strip("ab")` is ~30% faster and avoids multiple allocations in the hot path. **Action:** When checking if a string is solely composed of specific characters, use `.strip(chars)` instead of chained `.replace()` calls to improve performance. + +## 2024-07-31 - Bypass string allocation when reading JSON files +**Learning:** Passing `path.read_bytes()` directly to `json.loads()` is faster than `path.read_text()` because it bypasses the intermediate string allocation and decoding overhead. +**Action:** When reading JSON files, use `path.read_bytes()` for performance optimization. diff --git a/src/newsdom_api/mineru_runner.py b/src/newsdom_api/mineru_runner.py index 68bba5bd..e6e033eb 100644 --- a/src/newsdom_api/mineru_runner.py +++ b/src/newsdom_api/mineru_runner.py @@ -262,7 +262,8 @@ def _execute_mineru(cmd: list[str]) -> subprocess.CompletedProcess[str]: def _read_mineru_json(path: Path, *, artifact: str) -> Any: """Read a MinerU JSON artifact with safe, differentiated failure messages.""" try: - return json.loads(path.read_text(encoding="utf-8")) + # ⚡ Bolt: Read bytes directly to bypass intermediate string allocation and decoding overhead + return json.loads(path.read_bytes()) except json.JSONDecodeError as exc: raise MineruIncompleteOutputError(f"{artifact} JSON was malformed") from exc except (OSError, UnicodeDecodeError) as exc: diff --git a/tests/test_mineru_runner_paths.py b/tests/test_mineru_runner_paths.py index 427681b4..e76dedaf 100644 --- a/tests/test_mineru_runner_paths.py +++ b/tests/test_mineru_runner_paths.py @@ -286,14 +286,14 @@ def test_parse_mineru_output_distinguishes_read_failures( json.dumps([{"layout_dets": []}]), encoding="utf-8" ) - original_read_text = Path.read_text + original_read_bytes = Path.read_bytes - def fake_read_text(self, *args, **kwargs): + def fake_read_bytes(self, *args, **kwargs): if self.name == file_name: raise read_error - return original_read_text(self, *args, **kwargs) + return original_read_bytes(self, *args, **kwargs) - monkeypatch.setattr(Path, "read_text", fake_read_text) + monkeypatch.setattr(Path, "read_bytes", fake_read_bytes) with pytest.raises(MineruIncompleteOutputError, match=expected_detail) as exc_info: mineru_runner._parse_mineru_output(tmp_path, Path("sample.pdf")) @@ -613,14 +613,14 @@ class Result: return Result() - original_read_text = Path.read_text + original_read_bytes = Path.read_bytes - def fake_read_text(self, *args, **kwargs): + def fake_read_bytes(self, *args, **kwargs): if self.name == file_name: raise read_error - return original_read_text(self, *args, **kwargs) + return original_read_bytes(self, *args, **kwargs) - monkeypatch.setattr(Path, "read_text", fake_read_text) + monkeypatch.setattr(Path, "read_bytes", fake_read_bytes) monkeypatch.setattr(mineru_runner.subprocess, "run", fake_run) with pytest.raises(Exception) as exc_info: