From b555c6c861d17bee3c6d5c421492844f317ef6b1 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 11 Jun 2025 17:17:44 +0800 Subject: [PATCH 01/16] feat(cli): implement debug mode with coredumpy and viztracer --- Makefile | 2 +- pyproject.toml | 3 +-- src/vectorcode/cli_utils.py | 8 ++++++++ src/vectorcode/debugging.py | 38 +++++++++++++++++++++++++++++++++++++ src/vectorcode/main.py | 6 +++++- 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/vectorcode/debugging.py diff --git a/Makefile b/Makefile index 53babd00..147f2c85 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: multitest deps: - pdm lock --group dev --group lsp --group mcp; \ + pdm lock --group dev --group lsp --group mcp --group debug; \ pdm install test: diff --git a/pyproject.toml b/pyproject.toml index 31837117..49af6607 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,14 +61,12 @@ write_template = "__version__ = '{}' # pragma: no cover" dev = [ "ipython>=8.31.0", "ruff>=0.9.1", - "viztracer>=1.0.0", "pre-commit>=4.0.1", "pytest>=8.3.4", "pdm-backend>=2.4.3", "coverage>=7.6.12", "pytest-asyncio>=0.25.3", "debugpy>=1.8.12", - "coredumpy>=0.4.1", "basedpyright>=1.29.2", ] @@ -77,6 +75,7 @@ legacy = ["numpy<2.0.0", "torch==2.2.2", "transformers<=4.49.0"] intel = ['optimum[openvino]', 'openvino'] lsp = ['pygls<2.0.0', 'lsprotocol'] mcp = ['mcp<2.0.0', 'pydantic'] +debug = ["coredumpy>=0.4.1", "viztracer>=1.0.0"] [tool.basedpyright] typeCheckingMode = "standard" diff --git a/src/vectorcode/cli_utils.py b/src/vectorcode/cli_utils.py index c6b3cb5a..c170d652 100644 --- a/src/vectorcode/cli_utils.py +++ b/src/vectorcode/cli_utils.py @@ -64,6 +64,7 @@ class CliAction(Enum): @dataclass class Config: + debug: bool = False no_stderr: bool = False recursive: bool = False include_hidden: bool = False @@ -189,6 +190,12 @@ async def merge_from(self, other: "Config") -> "Config": def get_cli_parser(): __default_config = Config() shared_parser = argparse.ArgumentParser(add_help=False) + shared_parser.add_argument( + "--debug", + default=False, + action="store_true", + help="Enable debug mode (requires vectorcode[debug]).", + ) chunking_parser = argparse.ArgumentParser(add_help=False) chunking_parser.add_argument( "--overlap", @@ -397,6 +404,7 @@ async def parse_cli_args(args: Optional[Sequence[str]] = None): "action": CliAction(main_args.action), "project_root": main_args.project_root, "pipe": main_args.pipe, + "debug": main_args.debug, } match main_args.action: diff --git a/src/vectorcode/debugging.py b/src/vectorcode/debugging.py new file mode 100644 index 00000000..302f3773 --- /dev/null +++ b/src/vectorcode/debugging.py @@ -0,0 +1,38 @@ +import logging +import os +from datetime import datetime + +# import atexit + +__LOG_DIR = os.path.expanduser("~/.local/share/vectorcode/logs/") + +logger = logging.getLogger(name=__name__) + +__tracer = None + + +def finish(): + if __tracer is not None: + __tracer.stop() + __tracer.save( + output_file=os.path.join( + __LOG_DIR, + f"viztracer-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.json", + ) + ) + + +def enable(): + global __tracer + try: + import coredumpy + # import viztracer + except ModuleNotFoundError: + logger.warning("Failed to import modules. Please install vectorcode[debug]") + return + + coredumpy.patch_except(directory=__LOG_DIR) + # if __tracer is None: + # __tracer = viztracer.VizTracer(log_async=True) + # __tracer.start() + # atexit.register(finish) diff --git a/src/vectorcode/main.py b/src/vectorcode/main.py index 3b64cff4..67b646a4 100644 --- a/src/vectorcode/main.py +++ b/src/vectorcode/main.py @@ -4,7 +4,7 @@ import sys import traceback -from vectorcode import __version__ +from vectorcode import __version__, debugging from vectorcode.cli_utils import ( CliAction, config_logging, @@ -20,6 +20,10 @@ async def async_main(): cli_args = await parse_cli_args() if cli_args.no_stderr: sys.stderr = open(os.devnull, "w") + + if cli_args.debug: + debugging.enable() + logger.info("Collected CLI arguments: %s", cli_args) if cli_args.project_root is None: From 8d68912e4f751a766be518bbafbe88859f57a450 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 11 Jun 2025 17:32:16 +0800 Subject: [PATCH 02/16] chore(cli): exclude debugging.py from coverage --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 49af6607..20695465 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ omit = [ "./tests/*", "src/vectorcode/_version.py", "src/vectorcode/__init__.py", + "src/vectorcode/debugging.py", "/tmp/*", ] include = ['src/vectorcode/**/*.py'] From 375add5584943a1255fbd063283a141fc776f56f Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 11 Jun 2025 17:35:35 +0800 Subject: [PATCH 03/16] ci(cli): add debug group to pdm lock --- .github/workflows/test_and_cov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_and_cov.yml b/.github/workflows/test_and_cov.yml index 4ff9b509..434a083f 100644 --- a/.github/workflows/test_and_cov.yml +++ b/.github/workflows/test_and_cov.yml @@ -32,13 +32,13 @@ jobs: - name: install pdm and dependencies if: matrix.os != 'macos-14' run: | - pdm lock --group dev --group lsp --group mcp + pdm lock --group dev --group lsp --group mcp --group debug pdm install - name: install pdm and dependencies for legacy system if: matrix.os == 'macos-14' run: | - pdm lock --group dev --group lsp --group mcp --group legacy + pdm lock --group dev --group lsp --group mcp --group legacy --group debug pdm install - name: Set custom HF cache directory From 1dd9ff9a094a28e9d981a6972590b817e2a8d88a Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Wed, 13 Aug 2025 20:22:32 +0300 Subject: [PATCH 04/16] feat(debugging): Implement cProfile-based profiling for performance analysis Since viztracer didn't work (https://github.com/gaogaotiantian/viztracer/issues/606), I decided to replace it with cProfile --- src/vectorcode/debugging.py | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/vectorcode/debugging.py b/src/vectorcode/debugging.py index 302f3773..cd9930ea 100644 --- a/src/vectorcode/debugging.py +++ b/src/vectorcode/debugging.py @@ -1,38 +1,49 @@ import logging import os +import cProfile +import pstats from datetime import datetime -# import atexit +import atexit __LOG_DIR = os.path.expanduser("~/.local/share/vectorcode/logs/") logger = logging.getLogger(name=__name__) -__tracer = None +__profiler: cProfile.Profile | None = None def finish(): - if __tracer is not None: - __tracer.stop() - __tracer.save( - output_file=os.path.join( + """Clean up profiling and save results""" + if __profiler is not None: + try: + __profiler.disable() + stats_file = os.path.join( __LOG_DIR, - f"viztracer-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.json", + f"cprofile-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.stats", ) - ) + __profiler.dump_stats(stats_file) + logger.info(f"cProfile stats saved to: {stats_file}") + + # Print summary stats + stats = pstats.Stats(__profiler) + stats.sort_stats('cumulative') + stats.print_stats(20) + except Exception as e: + logger.warning(f"Failed to save cProfile output: {e}") def enable(): - global __tracer + """Enable cProfile-based profiling""" + global __profiler + try: - import coredumpy - # import viztracer - except ModuleNotFoundError: - logger.warning("Failed to import modules. Please install vectorcode[debug]") - return - - coredumpy.patch_except(directory=__LOG_DIR) - # if __tracer is None: - # __tracer = viztracer.VizTracer(log_async=True) - # __tracer.start() - # atexit.register(finish) + # Initialize cProfile for comprehensive profiling + __profiler = cProfile.Profile() + __profiler.enable() + atexit.register(finish) + logger.info("cProfile profiling enabled successfully") + + except Exception as e: + logger.error(f"Failed to initialize cProfile: {e}") + logger.warning("Profiling will not be available for this session") \ No newline at end of file From d3bc7d8f638e128d0a280669c1ed30d6d8324ac1 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Wed, 13 Aug 2025 20:34:40 +0300 Subject: [PATCH 05/16] chore(dependencies): Remove viztracer from debug dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 38253932..01fd3b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ legacy = ["numpy<2.0.0", "torch==2.2.2", "transformers<=4.49.0"] intel = ['optimum[openvino]', 'openvino'] lsp = ['pygls<2.0.0', 'lsprotocol'] mcp = ['mcp<2.0.0', 'pydantic'] -debug = ["coredumpy>=0.4.1", "viztracer>=1.0.0"] +debug = ["coredumpy>=0.4.1"] [tool.basedpyright] typeCheckingMode = "standard" From 9e9bcaef066535c56d1d17f38aba2b33f1958f9e Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Thu, 14 Aug 2025 19:10:43 +0800 Subject: [PATCH 06/16] fix(cli): lazy import --- src/vectorcode/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vectorcode/main.py b/src/vectorcode/main.py index 265394e9..345aedc5 100644 --- a/src/vectorcode/main.py +++ b/src/vectorcode/main.py @@ -4,9 +4,9 @@ import sys import traceback -from vectorcode import __version__, debugging import httpx +from vectorcode import __version__ from vectorcode.cli_utils import ( CliAction, config_logging, @@ -25,6 +25,8 @@ async def async_main(): sys.stderr = open(os.devnull, "w") if cli_args.debug: + from vectorcode import debugging + debugging.enable() logger.info("Collected CLI arguments: %s", cli_args) From 0bc07d76f2c779a903428e9ebe6cdabe9c08b857 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Thu, 14 Aug 2025 14:41:14 +0300 Subject: [PATCH 07/16] feat(debugging): add log directory creation and crash debugging support --- src/vectorcode/debugging.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vectorcode/debugging.py b/src/vectorcode/debugging.py index cd9930ea..3bfa02ba 100644 --- a/src/vectorcode/debugging.py +++ b/src/vectorcode/debugging.py @@ -13,6 +13,11 @@ __profiler: cProfile.Profile | None = None +def _ensure_log_dir(): + """Ensure the log directory exists""" + os.makedirs(__LOG_DIR, exist_ok=True) + + def finish(): """Clean up profiling and save results""" if __profiler is not None: @@ -23,7 +28,7 @@ def finish(): f"cprofile-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.stats", ) __profiler.dump_stats(stats_file) - logger.info(f"cProfile stats saved to: {stats_file}") + print(f"cProfile stats saved to: {stats_file}") # Print summary stats stats = pstats.Stats(__profiler) @@ -34,16 +39,24 @@ def finish(): def enable(): - """Enable cProfile-based profiling""" + """Enable cProfile-based profiling and crash debugging""" global __profiler try: + _ensure_log_dir() + # Initialize cProfile for comprehensive profiling __profiler = cProfile.Profile() __profiler.enable() atexit.register(finish) logger.info("cProfile profiling enabled successfully") + try: + import coredumpy + logger.info("coredumpy crash debugging enabled successfully") + except Exception as e: + logger.warning(f"Crash debugging will not be available. Failed to import coredumpy: {e}") + except Exception as e: logger.error(f"Failed to initialize cProfile: {e}") - logger.warning("Profiling will not be available for this session") \ No newline at end of file + logger.warning("Profiling will not be available for this session") From 4bbff7c7ccdbf98fac1536b854ce2c6dfb9cbb41 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Thu, 14 Aug 2025 14:51:54 +0300 Subject: [PATCH 08/16] chore(build): revert makefile and ci/cd outdated changes --- .github/workflows/test_and_cov.yml | 12 ------------ Makefile | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/test_and_cov.yml b/.github/workflows/test_and_cov.yml index c0608323..eaea1fee 100644 --- a/.github/workflows/test_and_cov.yml +++ b/.github/workflows/test_and_cov.yml @@ -29,18 +29,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: install pdm and dependencies - if: matrix.os != 'macos-14' - run: | - pdm lock --group dev --group lsp --group mcp --group debug - pdm install - - - name: install pdm and dependencies for legacy system - if: matrix.os == 'macos-14' - run: | - pdm lock --group dev --group lsp --group mcp --group legacy --group debug - pdm install - - name: Install uv uses: astral-sh/setup-uv@v5 with: diff --git a/Makefile b/Makefile index be8a87e3..4f06bf89 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ DEFAULT_GROUPS=--group dev --group lsp --group mcp deps: - pdm lock --group dev --group lsp --group mcp --group debug; \ + pdm lock $(DEFAULT_GROUPS) || pdm lock $(DEFAULT_GROUPS) --group legacy; \ pdm install test: From 7e92612f2268be4f89b394eb73a09358beeb8610 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Thu, 14 Aug 2025 15:00:29 +0300 Subject: [PATCH 09/16] chore(dependencies): Add coredumpy to development dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 01fd3b28..debe2456 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,7 @@ dev = [ "pytest-asyncio>=0.25.3", "debugpy>=1.8.12", "basedpyright>=1.29.2", + "coredumpy>=0.4.1", ] [project.optional-dependencies] @@ -78,7 +79,6 @@ legacy = ["numpy<2.0.0", "torch==2.2.2", "transformers<=4.49.0"] intel = ['optimum[openvino]', 'openvino'] lsp = ['pygls<2.0.0', 'lsprotocol'] mcp = ['mcp<2.0.0', 'pydantic'] -debug = ["coredumpy>=0.4.1"] [tool.basedpyright] typeCheckingMode = "standard" From 05c6b6a44e85e70e6e1838afad01acc869b01e01 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Thu, 14 Aug 2025 15:43:31 +0300 Subject: [PATCH 10/16] fix(debugging): Add noqa comment to suppress linting warning --- src/vectorcode/debugging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vectorcode/debugging.py b/src/vectorcode/debugging.py index 3bfa02ba..43f232da 100644 --- a/src/vectorcode/debugging.py +++ b/src/vectorcode/debugging.py @@ -52,7 +52,7 @@ def enable(): logger.info("cProfile profiling enabled successfully") try: - import coredumpy + import coredumpy # noqa: F401 logger.info("coredumpy crash debugging enabled successfully") except Exception as e: logger.warning(f"Crash debugging will not be available. Failed to import coredumpy: {e}") From 41a8b11434d831a5f98d0170ec4f4b22203aa050 Mon Sep 17 00:00:00 2001 From: Daniil Sobolev Date: Thu, 14 Aug 2025 16:05:25 +0300 Subject: [PATCH 11/16] docs(cli): remove debug dependency from cli help --- src/vectorcode/cli_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vectorcode/cli_utils.py b/src/vectorcode/cli_utils.py index 0097334a..7c49def2 100644 --- a/src/vectorcode/cli_utils.py +++ b/src/vectorcode/cli_utils.py @@ -203,7 +203,7 @@ def get_cli_parser(): "--debug", default=False, action="store_true", - help="Enable debug mode (requires vectorcode[debug]).", + help="Enable debug mode.", ) chunking_parser = argparse.ArgumentParser(add_help=False) chunking_parser.add_argument( From f491c0b53929410f57c0d3a4cc7176c43aaa85de Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Sun, 17 Aug 2025 16:00:59 +0800 Subject: [PATCH 12/16] fix(cli): make sure to enable `coredumpy` when available. --- src/vectorcode/debugging.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vectorcode/debugging.py b/src/vectorcode/debugging.py index 43f232da..7021fb4d 100644 --- a/src/vectorcode/debugging.py +++ b/src/vectorcode/debugging.py @@ -1,11 +1,10 @@ +import atexit +import cProfile import logging import os -import cProfile import pstats from datetime import datetime -import atexit - __LOG_DIR = os.path.expanduser("~/.local/share/vectorcode/logs/") logger = logging.getLogger(name=__name__) @@ -29,10 +28,10 @@ def finish(): ) __profiler.dump_stats(stats_file) print(f"cProfile stats saved to: {stats_file}") - + # Print summary stats stats = pstats.Stats(__profiler) - stats.sort_stats('cumulative') + stats.sort_stats("cumulative") stats.print_stats(20) except Exception as e: logger.warning(f"Failed to save cProfile output: {e}") @@ -41,7 +40,7 @@ def finish(): def enable(): """Enable cProfile-based profiling and crash debugging""" global __profiler - + try: _ensure_log_dir() @@ -50,12 +49,16 @@ def enable(): __profiler.enable() atexit.register(finish) logger.info("cProfile profiling enabled successfully") - + try: import coredumpy # noqa: F401 + logger.info("coredumpy crash debugging enabled successfully") + coredumpy.patch_except(directory=__LOG_DIR) except Exception as e: - logger.warning(f"Crash debugging will not be available. Failed to import coredumpy: {e}") + logger.warning( + f"Crash debugging will not be available. Failed to import coredumpy: {e}" + ) except Exception as e: logger.error(f"Failed to initialize cProfile: {e}") From 7bb3a660fb774f5953fd5b6a434bc8c14c4b974c Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Sun, 17 Aug 2025 16:35:58 +0800 Subject: [PATCH 13/16] tests(cli): Replace `MagicMock` with `Config` so that `debug` won't be truthy --- tests/test_main.py | 68 ++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 285427c9..b46c9989 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,9 +9,7 @@ @pytest.mark.asyncio async def test_async_main_no_stderr(monkeypatch): - mock_cli_args = MagicMock( - no_stderr=True, project_root=".", action=CliAction.version - ) + mock_cli_args = Config(no_stderr=True, project_root=".", action=CliAction.version) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -24,9 +22,7 @@ async def test_async_main_no_stderr(monkeypatch): @pytest.mark.asyncio async def test_async_main_default_project_root(monkeypatch): - mock_cli_args = MagicMock( - no_stderr=False, project_root=None, action=CliAction.version - ) + mock_cli_args = Config(no_stderr=False, project_root=None, action=CliAction.version) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -42,9 +38,7 @@ async def test_async_main_default_project_root(monkeypatch): @pytest.mark.asyncio async def test_async_main_ioerror(monkeypatch): - mock_cli_args = MagicMock( - no_stderr=False, project_root=".", action=CliAction.version - ) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.version) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -61,7 +55,7 @@ async def test_async_main_ioerror(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_check(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.check) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.check) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -79,7 +73,7 @@ async def test_async_main_cli_action_check(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_init(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.init) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.init) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -94,15 +88,15 @@ async def test_async_main_cli_action_init(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_chunks(monkeypatch): - mock_cli_args = MagicMock( - no_stderr=False, project_root=".", action=CliAction.chunks - ) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.chunks) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) mock_chunks = AsyncMock(return_value=0) monkeypatch.setattr("vectorcode.subcommands.chunks", mock_chunks) - monkeypatch.setattr("vectorcode.main.get_project_config", AsyncMock()) + monkeypatch.setattr( + "vectorcode.main.get_project_config", AsyncMock(return_value=Config()) + ) monkeypatch.setattr("vectorcode.common.try_server", AsyncMock(return_value=True)) return_code = await async_main() @@ -112,9 +106,7 @@ async def test_async_main_cli_action_chunks(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_version(monkeypatch, capsys): - mock_cli_args = MagicMock( - no_stderr=False, project_root=".", action=CliAction.version - ) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.version) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) @@ -127,13 +119,15 @@ async def test_async_main_cli_action_version(monkeypatch, capsys): @pytest.mark.asyncio async def test_async_main_cli_action_prompts(monkeypatch): - mock_cli_args = MagicMock(project_root=".", action=CliAction.prompts) + mock_cli_args = Config(project_root=".", action=CliAction.prompts) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) mock_prompts = MagicMock(return_value=0) monkeypatch.setattr("vectorcode.subcommands.prompts", mock_prompts) - monkeypatch.setattr("vectorcode.main.get_project_config", AsyncMock()) + monkeypatch.setattr( + "vectorcode.main.get_project_config", AsyncMock(return_value=Config()) + ) return_code = await async_main() assert return_code == 0 @@ -142,12 +136,12 @@ async def test_async_main_cli_action_prompts(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_query(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.query) + mock_cli_args = Config(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, pipe=False + mock_final_configs = Config( + db_url="http://test_host:1234", action=CliAction.query, pipe=False ) monkeypatch.setattr( "vectorcode.main.get_project_config", @@ -168,7 +162,7 @@ async def test_async_main_cli_action_query(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_vectorise(monkeypatch): - mock_cli_args = MagicMock( + mock_cli_args = Config( no_stderr=False, project_root=".", action=CliAction.vectorise, @@ -177,8 +171,8 @@ async def test_async_main_cli_action_vectorise(monkeypatch): monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - mock_final_configs = MagicMock( - host="test_host", port=1234, action=CliAction.vectorise, include_hidden=True + mock_final_configs = Config( + db_url="http://test_host:1234", action=CliAction.vectorise, include_hidden=True ) monkeypatch.setattr( "vectorcode.main.get_project_config", @@ -199,11 +193,11 @@ async def test_async_main_cli_action_vectorise(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_drop(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.drop) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.drop) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.drop) + mock_final_configs = Config(db_url="http://test_host:1234", action=CliAction.drop) monkeypatch.setattr( "vectorcode.main.get_project_config", AsyncMock( @@ -223,11 +217,11 @@ async def test_async_main_cli_action_drop(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_ls(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.ls) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.ls) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.ls) + mock_final_configs = Config(db_url="http://test_host:1234", action=CliAction.ls) monkeypatch.setattr( "vectorcode.main.get_project_config", AsyncMock( @@ -259,13 +253,11 @@ async def test_async_main_cli_action_files(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_update(monkeypatch): - mock_cli_args = MagicMock( - no_stderr=False, project_root=".", action=CliAction.update - ) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.update) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.update) + mock_final_configs = Config(db_url="http://test_host:1234", action=CliAction.update) monkeypatch.setattr( "vectorcode.main.get_project_config", AsyncMock( @@ -285,11 +277,11 @@ async def test_async_main_cli_action_update(monkeypatch): @pytest.mark.asyncio async def test_async_main_cli_action_clean(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.clean) + mock_cli_args = Config(no_stderr=False, project_root=".", action=CliAction.clean) monkeypatch.setattr( "vectorcode.main.parse_cli_args", AsyncMock(return_value=mock_cli_args) ) - mock_final_configs = MagicMock(host="test_host", port=1234, action=CliAction.clean) + mock_final_configs = Config(db_url="http://test_host:1234", action=CliAction.clean) monkeypatch.setattr( "vectorcode.main.get_project_config", AsyncMock( @@ -309,11 +301,11 @@ async def test_async_main_cli_action_clean(monkeypatch): @pytest.mark.asyncio async def test_async_main_exception_handling(monkeypatch): - mock_cli_args = MagicMock(no_stderr=False, project_root=".", action=CliAction.query) + mock_cli_args = Config(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) + mock_final_configs = Config(db_url="http://test_host:1234", action=CliAction.query) monkeypatch.setattr( "vectorcode.main.get_project_config", AsyncMock( From 1dde000c98186289ab87102a857c4e1af41f6d93 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Sun, 17 Aug 2025 16:37:35 +0800 Subject: [PATCH 14/16] docs(cli): Add profiling and post-mortem debugging instructions --- Makefile | 2 +- docs/cli.md | 17 +++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4f06bf89..048288b8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: multitest -DEFAULT_GROUPS=--group dev --group lsp --group mcp +DEFAULT_GROUPS=--group dev --group lsp --group mcp --group debug deps: pdm lock $(DEFAULT_GROUPS) || pdm lock $(DEFAULT_GROUPS) --group legacy; \ diff --git a/docs/cli.md b/docs/cli.md index a412e67a..1ab6d914 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -26,6 +26,8 @@ * [Cleaning up](#cleaning-up) * [Inspecting and Manupulating Files in an Indexed Project](#inspecting-and-manupulating-files-in-an-indexed-project) * [Debugging and Diagnosing](#debugging-and-diagnosing) + * [Profiling](#profiling) + * [Post-mortem debugging](#post-mortem-debugging) * [Shell Completion](#shell-completion) * [Hardware Acceleration](#hardware-acceleration) * [For Developers](#for-developers) @@ -551,6 +553,21 @@ VECTORCODE_LOG_LEVEL=INFO vectorcode vectorise file1.py file2.lua > Depending on the MCP/LSP client implementation, you may need to take extra > steps to make sure the environment variables are captured by VectorCode. +#### Profiling + +When you pass `--debug` parameter to the CLI, VectorCode will track the call +stacks with [cprofile](https://docs.python.org/3/library/profile.html). The +stats will be saved to the log directory mentioned above. You may use an +external stats viewer (like [snakeviz](https://jiffyclub.github.io/snakeviz/)) +to load the profiling stats for a better viewing experience. + +#### Post-mortem debugging + +VectorCode can work with [coredumpy](https://github.com/gaogaotiantian/coredumpy) +to snapshot an exception so that developers can inspect the error +asynchronously. To use this, you'd need to install the `vectorcode[debug]` +dependency group: `uv tool install vectorcode[debug]`. + ## Shell Completion VectorCode supports shell completion for bash/zsh/tcsh. You can use `vectorcode -s {bash,zsh,tcsh}` diff --git a/pyproject.toml b/pyproject.toml index debe2456..01fd3b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,6 @@ dev = [ "pytest-asyncio>=0.25.3", "debugpy>=1.8.12", "basedpyright>=1.29.2", - "coredumpy>=0.4.1", ] [project.optional-dependencies] @@ -79,6 +78,7 @@ legacy = ["numpy<2.0.0", "torch==2.2.2", "transformers<=4.49.0"] intel = ['optimum[openvino]', 'openvino'] lsp = ['pygls<2.0.0', 'lsprotocol'] mcp = ['mcp<2.0.0', 'pydantic'] +debug = ["coredumpy>=0.4.1"] [tool.basedpyright] typeCheckingMode = "standard" From a56f3b2e0ba383b02ebf1b1608932bbe2f69d02f Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Sun, 17 Aug 2025 17:11:48 +0800 Subject: [PATCH 15/16] ci: Update git auto commit action and ignore main branch --- .github/workflows/panvimdoc.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/panvimdoc.yml b/.github/workflows/panvimdoc.yml index 13938eaa..d76be516 100644 --- a/.github/workflows/panvimdoc.yml +++ b/.github/workflows/panvimdoc.yml @@ -1,7 +1,9 @@ name: panvimdoc on: - pull_request: + push: + branches-ignore: + - 'main' permissions: contents: write @@ -64,7 +66,8 @@ jobs: shiftheadinglevelby: 0 # Shift heading levels by specified number incrementheadinglevelby: 0 # Increment heading levels by specified number - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v6.0.1 with: commit_message: "Auto generate docs" branch: ${{ github.head_ref }} + file_pattern: 'doc/*.txt' From 5e30eb99e6b42b3303960799e8efe50f7a873353 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Sun, 17 Aug 2025 09:12:24 +0000 Subject: [PATCH 16/16] Auto generate docs --- doc/VectorCode-cli.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/VectorCode-cli.txt b/doc/VectorCode-cli.txt index 4b41a7af..66e2f0c3 100644 --- a/doc/VectorCode-cli.txt +++ b/doc/VectorCode-cli.txt @@ -37,6 +37,8 @@ Table of Contents *VectorCode-cli-table-of-contents* - |VectorCode-cli-cleaning-up| - |VectorCode-cli-inspecting-and-manupulating-files-in-an-indexed-project| - |VectorCode-cli-debugging-and-diagnosing| + - |VectorCode-cli-profiling| + - |VectorCode-cli-post-mortem-debugging| - |VectorCode-cli-shell-completion| - |VectorCode-cli-hardware-acceleration| - |VectorCode-cli-for-developers| @@ -605,6 +607,24 @@ For example: Depending on the MCP/LSP client implementation, you may need to take extra steps to make sure the environment variables are captured by VectorCode. +PROFILING + +When you pass `--debug` parameter to the CLI, VectorCode will track the call +stacks with cprofile . The +stats will be saved to the log directory mentioned above. You may use an +external stats viewer (like snakeviz ) +to load the profiling stats for a better viewing experience. + + +POST-MORTEM DEBUGGING + +VectorCode can work with coredumpy + to snapshot an exception so that +developers can inspect the error asynchronously. To use this, you’d need to +install the `vectorcode[debug]` dependency group: `uv tool install +vectorcode[debug]`. + + SHELL COMPLETION*VectorCode-cli-vectorcode-command-line-tool-shell-completion* VectorCode supports shell completion for bash/zsh/tcsh. You can use `vectorcode