From 581599a4f77281873eb207b0e70e7418a2dd7da4 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 14 May 2025 13:52:57 +0800 Subject: [PATCH 1/4] fix(cli): fix logging formatting. --- src/vectorcode/chunking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vectorcode/chunking.py b/src/vectorcode/chunking.py index ef13e8c0..dbcd0a7e 100644 --- a/src/vectorcode/chunking.py +++ b/src/vectorcode/chunking.py @@ -85,7 +85,7 @@ def __init__(self, config: Optional[Config] = None) -> None: super().__init__(config) def chunk(self, data: TextIOWrapper) -> Generator[Chunk, None, None]: - logger.info("Started chunking using FileChunker.", data.name) + logger.info("Started chunking %s using FileChunker.", data.name) lines = data.readlines() if len(lines) == 0: return From 65b166180fb82792a4a890adce430107db56f3c1 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 14 May 2025 14:02:29 +0800 Subject: [PATCH 2/4] chore(cli): remove redundant traceback printing. --- src/vectorcode/common.py | 4 +--- src/vectorcode/main.py | 13 ++++++++----- .../subcommands/query/reranker/__init__.py | 11 ----------- tests/test_main.py | 2 +- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/vectorcode/common.py b/src/vectorcode/common.py index d5a31954..231b8290 100644 --- a/src/vectorcode/common.py +++ b/src/vectorcode/common.py @@ -5,7 +5,6 @@ import socket import subprocess import sys -import traceback from typing import AsyncGenerator import chromadb @@ -159,9 +158,8 @@ def get_embedding_function(configs: Config) -> chromadb.EmbeddingFunction | None "\nFor errors caused by missing dependency, consult the documentation of pipx (or whatever package manager that you installed VectorCode with) for instructions to inject libraries into the virtual environment." ) logger.error( - f"Failed to use {configs.embedding_function} with the following error:", + f"Failed to use {configs.embedding_function} with following error.", ) - logger.error(traceback.format_exc()) raise diff --git a/src/vectorcode/main.py b/src/vectorcode/main.py index b930f4f7..9ff6008f 100644 --- a/src/vectorcode/main.py +++ b/src/vectorcode/main.py @@ -61,7 +61,7 @@ async def async_main(): case CliAction.chunks: from vectorcode.subcommands import chunks - return_val = await chunks(final_configs) + return await chunks(final_configs) case CliAction.hooks: logger.warning( "`vectorcode hooks` has been deprecated and will be removed in 0.7.0." @@ -109,10 +109,9 @@ async def async_main(): from vectorcode.subcommands import clean return_val = await clean(final_configs) - except Exception as e: + except Exception: return_val = 1 - traceback.print_exception(e, file=sys.stderr) - logger.error(traceback.format_exc()) + raise finally: if server_process is not None: logger.info("Shutting down the bundled Chromadb instance.") @@ -123,7 +122,11 @@ async def async_main(): def main(): # pragma: nocover config_logging("vectorcode") - return asyncio.run(async_main()) + try: + return asyncio.run(async_main()) + except Exception: + logger.error(traceback.format_exc()) + return 1 if __name__ == "__main__": # pragma: nocover diff --git a/src/vectorcode/subcommands/query/reranker/__init__.py b/src/vectorcode/subcommands/query/reranker/__init__.py index 64b67821..466e96f3 100644 --- a/src/vectorcode/subcommands/query/reranker/__init__.py +++ b/src/vectorcode/subcommands/query/reranker/__init__.py @@ -41,13 +41,11 @@ class CustomReranker(RerankerBase): if issubclass(cls, RerankerBase): if __supported_rerankers.get(cls.__name__): error_message = f"{cls.__name__} has been registered." - logger.error(error_message) raise AttributeError(error_message) __supported_rerankers[cls.__name__] = cls return cls else: error_message = f'{cls} should be a subclass of "RerankerBase"' - logger.error(error_message) raise TypeError(error_message) @@ -66,16 +64,7 @@ def get_reranker(configs: Config) -> RerankerBase: ): return __supported_rerankers[configs.reranker].create(configs) - # TODO: replace the following with an Exception before the release of 0.6.0. - logger.warning( - f""""reranker" option should be set to one of the following: {list(i.__name__ for i in get_available_rerankers())}. -To choose a CrossEncoderReranker model, you can set the "model_name_or_path" key in the "reranker_params" option to the name/path of the model. -To use NaiveReranker, set the "reranker" option to "NaiveReranker". -The old configuration syntax will be DEPRECATED in v0.6.0 - """ - ) if not configs.reranker: return NaiveReranker(configs) else: - logger.error(f"{configs.reranker} is not a valid reranker type!") raise RerankerInitialisationError() diff --git a/tests/test_main.py b/tests/test_main.py index 8b990684..eb6457be 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -374,7 +374,7 @@ async def test_async_main_exception_handling(monkeypatch): monkeypatch.setattr("vectorcode.subcommands.query", mock_query) with patch("sys.stderr.write") as mock_stderr: - return_code = await async_main() + return_code = main() assert return_code == 1 mock_stderr.assert_called() From 23ed7338f4758d1970ba567babb2e30f75f2821a Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 14 May 2025 16:20:22 +0800 Subject: [PATCH 3/4] fix broken tests --- tests/test_main.py | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index eb6457be..43e3e58a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -354,29 +354,24 @@ async def test_async_main_cli_action_clean(monkeypatch): mock_clean.assert_called_once_with(mock_final_configs) -@pytest.mark.asyncio -async def test_async_main_exception_handling(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.query) - monkeypatch.setattr( - "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) - ) - mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.query) - monkeypatch.setattr( - "vectorcode.main.get_project_config", - AsyncMock( - return_value=AsyncMock( - merge_from=AsyncMock(return_value=mock_final_configs) - ) - ), +def test_main_exception_handling(monkeypatch): + mock_async_main_with_exception = AsyncMock( + side_effect=Exception("Simulated Error in async_main") ) - monkeypatch.setattr("vectorcode.common.try_server", AsyncMock(return_value=True)) - mock_query = AsyncMock(side_effect=Exception("Test Exception")) - monkeypatch.setattr("vectorcode.subcommands.query", mock_query) + monkeypatch.setattr("vectorcode.main.async_main", mock_async_main_with_exception) - with patch("sys.stderr.write") as mock_stderr: - return_code = main() - assert return_code == 1 - mock_stderr.assert_called() + mock_asyncio_run = MagicMock(side_effect=lambda coro: coro.result()) + monkeypatch.setattr("asyncio.run", mock_asyncio_run) + + with patch("vectorcode.main.logger") as mock_logger: + result = main() + + assert result == 1 + + mock_logger.error.assert_called() # You might want to check specific arguments too + + mock_async_main_with_exception.assert_called_once() + mock_asyncio_run.assert_called_once() @pytest.mark.asyncio From ae2149df97a1dcc5928874732847c31b0c13a726 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 14 May 2025 19:08:14 +0800 Subject: [PATCH 4/4] make sure errors are logged. --- src/vectorcode/main.py | 8 ++------ tests/test_main.py | 34 +++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/vectorcode/main.py b/src/vectorcode/main.py index 9ff6008f..b8f15aab 100644 --- a/src/vectorcode/main.py +++ b/src/vectorcode/main.py @@ -111,7 +111,7 @@ async def async_main(): return_val = await clean(final_configs) except Exception: return_val = 1 - raise + logger.error(traceback.format_exc()) finally: if server_process is not None: logger.info("Shutting down the bundled Chromadb instance.") @@ -122,11 +122,7 @@ async def async_main(): def main(): # pragma: nocover config_logging("vectorcode") - try: - return asyncio.run(async_main()) - except Exception: - logger.error(traceback.format_exc()) - return 1 + return asyncio.run(async_main()) if __name__ == "__main__": # pragma: nocover diff --git a/tests/test_main.py b/tests/test_main.py index 43e3e58a..d9d688b5 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -354,24 +354,28 @@ async def test_async_main_cli_action_clean(monkeypatch): mock_clean.assert_called_once_with(mock_final_configs) -def test_main_exception_handling(monkeypatch): - mock_async_main_with_exception = AsyncMock( - side_effect=Exception("Simulated Error in async_main") +@pytest.mark.asyncio +async def test_async_main_exception_handling(monkeypatch): + mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.query) + monkeypatch.setattr( + "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - monkeypatch.setattr("vectorcode.main.async_main", mock_async_main_with_exception) - - mock_asyncio_run = MagicMock(side_effect=lambda coro: coro.result()) - monkeypatch.setattr("asyncio.run", mock_asyncio_run) + mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.query) + monkeypatch.setattr( + "vectorcode.main.get_project_config", + AsyncMock( + return_value=AsyncMock( + merge_from=AsyncMock(return_value=mock_final_configs) + ) + ), + ) + monkeypatch.setattr("vectorcode.common.try_server", AsyncMock(return_value=True)) + mock_query = AsyncMock(side_effect=Exception("Test Exception")) + monkeypatch.setattr("vectorcode.subcommands.query", mock_query) with patch("vectorcode.main.logger") as mock_logger: - result = main() - - assert result == 1 - - mock_logger.error.assert_called() # You might want to check specific arguments too - - mock_async_main_with_exception.assert_called_once() - mock_asyncio_run.assert_called_once() + assert await async_main() == 1 + mock_logger.error.assert_called_once() @pytest.mark.asyncio