From afd57019377019b237bf3256b2ee2aab8b533850 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Tue, 20 Oct 2026 21:36:41 -0400 Subject: [PATCH 1/3] Modify json output parsing --- CHANGELOG.md | 1 + README.md | 14 +++++++++----- bundled/tool/lsp_server.py | 8 ++++---- src/test/python_tests/test_lsp_server_helpers.py | 15 ++++++++++++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcc011..f26149b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Updated publisher name from `` to `david-yz-liu` - Fixed `substitute_attr` in the bundled language server not restoring the original value when the wrapped code raised an exception +- Parse PythonTA's complete standard output as JSON and log an error when the output is malformed. ### 🔧 Internal changes diff --git a/README.md b/README.md index 1d8b857..f097d60 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,13 @@ This project is an extension for running [PythonTA](https://www.cs.toronto.edu/~ By default, `python-ta.useProjectRootConfig` automatically uses a PythonTA configuration file from the root of the current workspace. The extension checks for `.pylintrc`, `pylintrc`, and `pyproject.toml`, in that order. -When project-root configuration discovery is enabled, `python-ta.configPath` is ignored. +When project-root configuration discovery is enabled, `python-ta.configPath` is ignored. When discovery is disabled, `python-ta.configPath` specifies the configuration file instead. +The extension runs PythonTA with its LSP reporter and expects standard output to contain a complete +JSON document. PythonTA error output and JSON parsing errors are available in the +**PythonTA VS Code Extension** output channel. + ## Developers ### Requirements @@ -33,16 +37,16 @@ When discovery is disabled, `python-ta.configPath` specifies the configuration f 1. To start the extension, use the `Debug Extension and Python` (shortcut `F5`) configuration in VS Code. 2. In the new VS Code window that appears, open a folder containing Python files, or create a new file. 3. Open a file with a snippet or write a snippet of Python code that violates standard PythonTA rule. For example: - ```python - def add_numbers(a, b): - return a + c + ```python + def add_numbers(a, b): + return a + c ``` 4. Save the file, and look for the diagnostic messages and rule codes generated by PythonTA. 5. You can view the extension output under the Output tab, and select "PythonTA VS Code Extension" ### Troubleshooting: Server Crashes on File Reload -If you are actively testing the extension using the debugger (`F5`), you may notice that closing and reopening a Python file causes the server to crash repeatedly with a `ConnectionRefusedError`. +If you are actively testing the extension using the debugger (`F5`), you may notice that closing and reopening a Python file causes the server to crash repeatedly with a `ConnectionRefusedError`. **This is expected behavior in the development environment and will not happen in the published extension.** It occurs because the VS Code Extension Development Host wraps the language server in a `debugpy` instance that fails to cleanly release its port when the file reloads. diff --git a/bundled/tool/lsp_server.py b/bundled/tool/lsp_server.py index 183cfa7..9ca564a 100644 --- a/bundled/tool/lsp_server.py +++ b/bundled/tool/lsp_server.py @@ -247,12 +247,12 @@ def _linting_helper(document: workspace.Document) -> list[lsp.Diagnostic]: def _parse_json_output(content: str, doc_uri: str, use_stdin: bool = False) -> list[lsp.Diagnostic]: """Parses PythonTA's JSON output and maps it to LSP Diagnostics.""" - json_start = content.find("[") - if json_start == -1: + try: + raw_results = json.loads(content) + except json.JSONDecodeError as error: + log_error(f"Failed to parse PythonTA output as JSON: {error}") return [] - content = content[json_start:] - raw_results = json.loads(content) diagnostics_data = [] if use_stdin: diff --git a/src/test/python_tests/test_lsp_server_helpers.py b/src/test/python_tests/test_lsp_server_helpers.py index 4414ce6..da3454e 100644 --- a/src/test/python_tests/test_lsp_server_helpers.py +++ b/src/test/python_tests/test_lsp_server_helpers.py @@ -121,10 +121,15 @@ def test_parse_json_output_returns_stdin_diagnostics_despite_temporary_uri(): assert result[0].code == "E0001" -def test_parse_json_output_ignores_leading_log_lines(): +def test_parse_json_output_rejects_leading_log_lines(monkeypatch): + errors = [] + monkeypatch.setattr(lsp_server, "log_error", errors.append) content = "some log preamble\nmore logs\n" + json.dumps(_make_payload()) result = lsp_server._parse_json_output(content, "file:///home/user/project/foo.py") - assert len(result) == 1 + + assert result == [] + assert len(errors) == 1 + assert "Failed to parse PythonTA output as JSON" in errors[0] def test_parse_json_output_returns_empty_list_for_unmatched_uri(): @@ -133,8 +138,12 @@ def test_parse_json_output_returns_empty_list_for_unmatched_uri(): assert result == [] -def test_parse_json_output_returns_empty_list_when_no_json_array_present(): +def test_parse_json_output_reports_invalid_json(monkeypatch): + errors = [] + monkeypatch.setattr(lsp_server, "log_error", errors.append) + assert lsp_server._parse_json_output("no json here", "file:///x.py") == [] + assert len(errors) == 1 def test_parse_json_output_returns_empty_list_for_file_with_no_diagnostics(): From 74cc613e0d1358e32e6e9246676ac1501d90b9df Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Tue, 20 Oct 2026 21:50:40 -0400 Subject: [PATCH 2/3] Rerun CI From aa0199b203993f79cb9629a202658cb0318c8543 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Tue, 20 Oct 2026 22:05:01 -0400 Subject: [PATCH 3/3] Formatting revert --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f097d60..3bbe47c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This project is an extension for running [PythonTA](https://www.cs.toronto.edu/~ By default, `python-ta.useProjectRootConfig` automatically uses a PythonTA configuration file from the root of the current workspace. The extension checks for `.pylintrc`, `pylintrc`, and `pyproject.toml`, in that order. -When project-root configuration discovery is enabled, `python-ta.configPath` is ignored. +When project-root configuration discovery is enabled, `python-ta.configPath` is ignored. When discovery is disabled, `python-ta.configPath` specifies the configuration file instead. The extension runs PythonTA with its LSP reporter and expects standard output to contain a complete @@ -37,16 +37,16 @@ JSON document. PythonTA error output and JSON parsing errors are available in th 1. To start the extension, use the `Debug Extension and Python` (shortcut `F5`) configuration in VS Code. 2. In the new VS Code window that appears, open a folder containing Python files, or create a new file. 3. Open a file with a snippet or write a snippet of Python code that violates standard PythonTA rule. For example: - ```python - def add_numbers(a, b): - return a + c + ```python + def add_numbers(a, b): + return a + c ``` 4. Save the file, and look for the diagnostic messages and rule codes generated by PythonTA. 5. You can view the extension output under the Output tab, and select "PythonTA VS Code Extension" ### Troubleshooting: Server Crashes on File Reload -If you are actively testing the extension using the debugger (`F5`), you may notice that closing and reopening a Python file causes the server to crash repeatedly with a `ConnectionRefusedError`. +If you are actively testing the extension using the debugger (`F5`), you may notice that closing and reopening a Python file causes the server to crash repeatedly with a `ConnectionRefusedError`. **This is expected behavior in the development environment and will not happen in the published extension.** It occurs because the VS Code Extension Development Host wraps the language server in a `debugpy` instance that fails to cleanly release its port when the file reloads.