From 76f61a0d61ea9541df7854b55d7713c3695509e8 Mon Sep 17 00:00:00 2001 From: Michael Ding Date: Wed, 27 May 2026 11:18:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8D=95=E4=B8=AA=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=BC=82=E5=B8=B8=E4=B8=8D=E5=BA=94=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E6=95=B4=E4=B8=AA=20bulk-convert=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 except ValueError 改为 except Exception,防止 RuntimeError 等 非 ValueError 异常中断批量转换 - 新增 test_bulk_convert_runtime_error_continues_r 和 test_bulk_convert_runtime_error_continues_f 覆盖此场景 --- src/kbmate_cli/main.py | 4 ++-- tests/test_cli.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/kbmate_cli/main.py b/src/kbmate_cli/main.py index ea9279c..6d74451 100644 --- a/src/kbmate_cli/main.py +++ b/src/kbmate_cli/main.py @@ -164,7 +164,7 @@ def bulk_convert( for src_path in _collect_files_from_dir(root): try: convert_single(src_path, out, layout=output_layout, relative_to=root) - except ValueError as e: + except Exception as e: typer.echo(f"Error converting {src_path}: {e}", err=True) else: assert file_list is not None @@ -193,7 +193,7 @@ def bulk_convert( continue try: convert_single(src, out, layout=output_layout) - except ValueError as e: + except Exception as e: typer.echo(f"Error converting {src}: {e}", err=True) finally: if temp_path: diff --git a/tests/test_cli.py b/tests/test_cli.py index d738c1a..89c2b6b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -226,6 +226,55 @@ def test_bulk_convert_invalid_layout(tmp_path): assert "must be 'flat' or 'mirror'" in result.stderr.lower() +@patch.dict("kbmate_cli.main._CONVERTERS", {".pdf": MagicMock(side_effect=[RuntimeError("unexpected crash"), "# mock"])}) +def test_bulk_convert_runtime_error_continues_r(): + """RuntimeError from converter should not crash the entire -r batch""" + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + pdf1 = root / "a.pdf" + pdf2 = root / "b.pdf" + pdf1.write_text("fake") + pdf2.write_text("fake") + + out = root / "out" + result = runner.invoke(app, [ + "bulk-convert", "-r", str(root), + "--output-dir", str(out), + ]) + assert result.exit_code == 0 + assert "Error converting" in result.stderr + converted = list(out.rglob("*.md")) + assert len(converted) == 1 + + +@patch("kbmate_cli.main._resolve_source") +@patch.dict("kbmate_cli.main._CONVERTERS", {".pdf": MagicMock(side_effect=[RuntimeError("unexpected crash"), "# mock"])}) +def test_bulk_convert_runtime_error_continues_f(mock_resolve, tmp_path): + """RuntimeError from converter should not crash the entire -f batch""" + root = tmp_path + pdf1 = root / "a.pdf" + pdf2 = root / "b.pdf" + pdf1.write_text("fake") + pdf2.write_text("fake") + mock_resolve.side_effect = [ + (str(pdf1), None), + (str(pdf2), None), + ] + + flist = root / "sources.txt" + flist.write_text("/path/to/a.pdf\n/path/to/b.pdf\n") + + out = root / "out" + result = runner.invoke(app, [ + "bulk-convert", "-f", str(flist), + "--output-dir", str(out), + ]) + assert result.exit_code == 0 + assert "Error converting" in result.stderr + converted = list(out.rglob("*.md")) + assert len(converted) == 1 + + @patch.dict("kbmate_cli.main._CONVERTERS", {".pdf": MagicMock(side_effect=[ValueError("mock fail"), "# mock"])}) def test_bulk_convert_continue_on_error(): with tempfile.TemporaryDirectory() as tmp: