From 28e542b26b575c23e89cd1ee61350c8ce3f9ecc1 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Wed, 14 Oct 2026 12:41:38 -0400 Subject: [PATCH 1/4] Added config file logic --- bundled/tool/lsp_server.py | 25 +++++++++- package.json | 6 +++ src/common/settings.ts | 4 ++ .../python_tests/test_lsp_server_helpers.py | 48 +++++++++++++++++++ src/test/suite/settings.test.ts | 1 + 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/bundled/tool/lsp_server.py b/bundled/tool/lsp_server.py index 62b820e..ada8ed4 100644 --- a/bundled/tool/lsp_server.py +++ b/bundled/tool/lsp_server.py @@ -85,6 +85,7 @@ def update_sys_path(path_to_add: str, strategy: str) -> None: TOOL_DISPLAY = "PythonTA" TOOL_ARGS = ["--output-format", "pyta-lsp", "--exit-zero"] # default arguments always passed to your tool +PROJECT_ROOT_CONFIG_FILENAMES = (".pylintrc", "pylintrc", "pyproject.toml") # TODO: If your tool is a linter then update this section. # Delete "Linting features" section if your tool is NOT a linter. @@ -387,6 +388,7 @@ def _get_global_defaults(): "importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"), "showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"), "configPath": GLOBAL_SETTINGS.get("configPath", ""), + "useProjectRootConfig": GLOBAL_SETTINGS.get("useProjectRootConfig", False), } @@ -455,6 +457,27 @@ def _get_settings_by_document(document: workspace.TextDocument | None): return WORKSPACE_SETTINGS[str(key)] +def _find_project_root_config(project_root: str, config_filenames: Sequence[str]) -> str | None: + """Return the first config file from inputted filenames that's found in the project root.""" + root = pathlib.Path(project_root) + for filename in config_filenames: + config_path = root / filename + if config_path.is_file(): + return os.fspath(config_path) + + return None + + +def _get_config_path(settings: dict[str, Any]) -> str | None: + """Return the discovered project-root config or the configured path.""" + if settings.get("useProjectRootConfig", False): + return _find_project_root_config( + settings["workspaceFS"], PROJECT_ROOT_CONFIG_FILENAMES + ) + + return settings.get("configPath") or None + + # ***************************************************** # Internal execution APIs. # ***************************************************** @@ -507,7 +530,7 @@ def _run_tool_on_document( argv += TOOL_ARGS + settings["args"] + extra_args - config_path = settings.get("configPath") + config_path = _get_config_path(settings) if config_path: argv += ["--config", config_path] diff --git a/package.json b/package.json index 975a239..ca5c704 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,12 @@ "description": "Absolute or relative path to your PythonTA configuration file (e.g., .pyta_config.ini).", "scope": "resource" }, + "python-ta.useProjectRootConfig": { + "type": "boolean", + "default": false, + "description": "Automatically use a supported PythonTA configuration file from the workspace root. When enabled, python-ta.configPath is ignored.", + "scope": "resource" + }, "python-ta.cwd": { "default": "${workspaceFolder}", "description": "Sets the working directory for python-ta. Supported variables: `${workspaceFolder}` (workspace root) and `${fileDirname}` (directory of the current file).", diff --git a/src/common/settings.ts b/src/common/settings.ts index 831309c..ec84bae 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -11,6 +11,7 @@ export interface ISettings { importStrategy: string; showNotifications: string; configPath: string; + useProjectRootConfig: boolean; } export function getExtensionSettings(namespace: string, includeInterpreter?: boolean): Promise { @@ -73,6 +74,7 @@ export async function getWorkspaceSettings( importStrategy: config.get(`importStrategy`) ?? 'useBundled', showNotifications: config.get(`showNotifications`) ?? 'off', configPath: config.get('configPath', ''), + useProjectRootConfig: config.get('useProjectRootConfig', false), }; return workspaceSetting; } @@ -102,6 +104,7 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?: importStrategy: getGlobalValue(config, 'importStrategy', 'useBundled'), showNotifications: getGlobalValue(config, 'showNotifications', 'off'), configPath: getGlobalValue(config, 'configPath', ''), + useProjectRootConfig: getGlobalValue(config, 'useProjectRootConfig', false), }; return setting; } @@ -115,6 +118,7 @@ export function checkIfConfigurationChanged(e: ConfigurationChangeEvent, namespa `${namespace}.importStrategy`, `${namespace}.showNotifications`, `${namespace}.configPath`, + `${namespace}.useProjectRootConfig`, ]; const changed = settings.map((s) => e.affectsConfiguration(s)); return changed.includes(true); diff --git a/src/test/python_tests/test_lsp_server_helpers.py b/src/test/python_tests/test_lsp_server_helpers.py index 73aa079..f91496a 100644 --- a/src/test/python_tests/test_lsp_server_helpers.py +++ b/src/test/python_tests/test_lsp_server_helpers.py @@ -143,6 +143,53 @@ def test_parse_json_output_returns_empty_list_for_file_with_no_diagnostics(): assert result == [] +# --------------------------------------------------------------------------- +# Project-root config discovery and selection +# --------------------------------------------------------------------------- + + +def test_get_config_path_uses_pyta_filename_priority_when_enabled(tmp_path): + (tmp_path / "pyproject.toml").touch() + (tmp_path / "pylintrc").touch() + highest_priority = tmp_path / ".pylintrc" + highest_priority.touch() + settings = { + "useProjectRootConfig": True, + "configPath": os.fspath(tmp_path / "manual.toml"), + "workspaceFS": os.fspath(tmp_path), + } + + result = lsp_server._get_config_path(settings) + + assert result == os.fspath(highest_priority) + + +def test_get_config_path_ignores_config_path_when_discovery_finds_nothing(tmp_path): + settings = { + "useProjectRootConfig": True, + "configPath": os.fspath(tmp_path / "manual.toml"), + "workspaceFS": os.fspath(tmp_path), + } + + result = lsp_server._get_config_path(settings) + + assert result is None + + +def test_get_config_path_uses_config_path_when_discovery_is_disabled(tmp_path): + (tmp_path / ".pylintrc").touch() + manual_config = tmp_path / "manual.toml" + settings = { + "useProjectRootConfig": False, + "configPath": os.fspath(manual_config), + "workspaceFS": os.fspath(tmp_path), + } + + result = lsp_server._get_config_path(settings) + + assert result == os.fspath(manual_config) + + # --------------------------------------------------------------------------- # _get_global_defaults # --------------------------------------------------------------------------- @@ -155,6 +202,7 @@ def test_get_global_defaults_uses_builtin_fallbacks_when_empty(): assert defaults["importStrategy"] == "useBundled" assert defaults["showNotifications"] == "off" assert defaults["configPath"] == "" + assert defaults["useProjectRootConfig"] is False def test_get_global_defaults_reflects_global_settings_overrides(): diff --git a/src/test/suite/settings.test.ts b/src/test/suite/settings.test.ts index 42287a6..49ea6a1 100644 --- a/src/test/suite/settings.test.ts +++ b/src/test/suite/settings.test.ts @@ -12,6 +12,7 @@ suite('settings.getGlobalSettings', () => { assert.strictEqual(settings.importStrategy, 'useBundled'); assert.strictEqual(settings.showNotifications, 'off'); assert.strictEqual(settings.configPath, ''); + assert.strictEqual(settings.useProjectRootConfig, false); }); }); From cbbc311c05ad507ce615586bb91707abc5aef420 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Wed, 14 Oct 2026 14:56:06 -0400 Subject: [PATCH 2/4] Changelog + Readme update --- CHANGELOG.md | 1 + README.md | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d92754..33aa3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Implement initial PythonTA LSP server - Add initial PythonTA implementation that simply calls the CLI - Lint Python documents from their editor contents through standard input +- Add optional discovery of PythonTA configuration files in the workspace root ### 🐛 Bug fixes diff --git a/README.md b/README.md index b3161f4..70fb149 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ This project is an extension for running [PythonTA](https://www.cs.toronto.edu/~david/pyta/) within VS Code. +## Configuration + +Set `python-ta.useProjectRootConfig` to `true` to automatically use 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 it is disabled (the default), `python-ta.configPath` continues to specify the configuration file. + ## Developers ### Requirements From c5df379eddf6bbf6082c9979f1090e2cae6af2d3 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Sat, 17 Oct 2026 23:23:24 -0400 Subject: [PATCH 3/4] Setting order and default change --- CHANGELOG.md | 2 +- README.md | 4 ++-- bundled/tool/lsp_server.py | 4 ++-- package.json | 19 +++++++++++-------- src/common/settings.ts | 4 ++-- .../python_tests/test_lsp_server_helpers.py | 2 +- src/test/suite/settings.test.ts | 2 +- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33aa3b1..ffcc011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Implement initial PythonTA LSP server - Add initial PythonTA implementation that simply calls the CLI - Lint Python documents from their editor contents through standard input -- Add optional discovery of PythonTA configuration files in the workspace root +- Add automatic discovery of PythonTA configuration files in the workspace root ### 🐛 Bug fixes diff --git a/README.md b/README.md index 70fb149..1d8b857 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ This project is an extension for running [PythonTA](https://www.cs.toronto.edu/~ ## Configuration -Set `python-ta.useProjectRootConfig` to `true` to automatically use a PythonTA configuration file from the root of the current workspace. +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 it is disabled (the default), `python-ta.configPath` continues to specify the configuration file. +When discovery is disabled, `python-ta.configPath` specifies the configuration file instead. ## Developers diff --git a/bundled/tool/lsp_server.py b/bundled/tool/lsp_server.py index ada8ed4..183cfa7 100644 --- a/bundled/tool/lsp_server.py +++ b/bundled/tool/lsp_server.py @@ -388,7 +388,7 @@ def _get_global_defaults(): "importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"), "showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"), "configPath": GLOBAL_SETTINGS.get("configPath", ""), - "useProjectRootConfig": GLOBAL_SETTINGS.get("useProjectRootConfig", False), + "useProjectRootConfig": GLOBAL_SETTINGS.get("useProjectRootConfig", True), } @@ -470,7 +470,7 @@ def _find_project_root_config(project_root: str, config_filenames: Sequence[str] def _get_config_path(settings: dict[str, Any]) -> str | None: """Return the discovered project-root config or the configured path.""" - if settings.get("useProjectRootConfig", False): + if settings.get("useProjectRootConfig", True): return _find_project_root_config( settings["workspaceFS"], PROJECT_ROOT_CONFIG_FILENAMES ) diff --git a/package.json b/package.json index ca5c704..61162c5 100644 --- a/package.json +++ b/package.json @@ -69,16 +69,18 @@ "contributes": { "configuration": { "properties": { - "python-ta.configPath": { - "type": "string", - "default": "", - "description": "Absolute or relative path to your PythonTA configuration file (e.g., .pyta_config.ini).", - "scope": "resource" - }, "python-ta.useProjectRootConfig": { "type": "boolean", - "default": false, + "default": true, "description": "Automatically use a supported PythonTA configuration file from the workspace root. When enabled, python-ta.configPath is ignored.", + "order": 1, + "scope": "resource" + }, + "python-ta.configPath": { + "type": "string", + "default": "", + "description": "Absolute or relative path to your PythonTA configuration file (e.g., .pyta_config.ini). This setting is ignored when python-ta.useProjectRootConfig is enabled.", + "order": 2, "scope": "resource" }, "python-ta.cwd": { @@ -182,5 +184,6 @@ "typescript": "^6.0.3", "webpack": "^5.111.1", "webpack-cli": "^7.2.3" - } + }, + "packageManager": "pnpm@12.6.0+sha512.3ef68f951cb111ac204b4a5a16f0b2ddf0da56a96e0413e81d855d9f0b55ef926714709028e1cd00c405c2c5fb7b9e8ec4dc46777c805d0373c2f2ff00fd20ec" } diff --git a/src/common/settings.ts b/src/common/settings.ts index ec84bae..b16fd37 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -74,7 +74,7 @@ export async function getWorkspaceSettings( importStrategy: config.get(`importStrategy`) ?? 'useBundled', showNotifications: config.get(`showNotifications`) ?? 'off', configPath: config.get('configPath', ''), - useProjectRootConfig: config.get('useProjectRootConfig', false), + useProjectRootConfig: config.get('useProjectRootConfig', true), }; return workspaceSetting; } @@ -104,7 +104,7 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?: importStrategy: getGlobalValue(config, 'importStrategy', 'useBundled'), showNotifications: getGlobalValue(config, 'showNotifications', 'off'), configPath: getGlobalValue(config, 'configPath', ''), - useProjectRootConfig: getGlobalValue(config, 'useProjectRootConfig', false), + useProjectRootConfig: getGlobalValue(config, 'useProjectRootConfig', true), }; return setting; } diff --git a/src/test/python_tests/test_lsp_server_helpers.py b/src/test/python_tests/test_lsp_server_helpers.py index f91496a..4414ce6 100644 --- a/src/test/python_tests/test_lsp_server_helpers.py +++ b/src/test/python_tests/test_lsp_server_helpers.py @@ -202,7 +202,7 @@ def test_get_global_defaults_uses_builtin_fallbacks_when_empty(): assert defaults["importStrategy"] == "useBundled" assert defaults["showNotifications"] == "off" assert defaults["configPath"] == "" - assert defaults["useProjectRootConfig"] is False + assert defaults["useProjectRootConfig"] is True def test_get_global_defaults_reflects_global_settings_overrides(): diff --git a/src/test/suite/settings.test.ts b/src/test/suite/settings.test.ts index 49ea6a1..f4cf03e 100644 --- a/src/test/suite/settings.test.ts +++ b/src/test/suite/settings.test.ts @@ -12,7 +12,7 @@ suite('settings.getGlobalSettings', () => { assert.strictEqual(settings.importStrategy, 'useBundled'); assert.strictEqual(settings.showNotifications, 'off'); assert.strictEqual(settings.configPath, ''); - assert.strictEqual(settings.useProjectRootConfig, false); + assert.strictEqual(settings.useProjectRootConfig, true); }); }); From 5d12a968598c83b4491fd0bb03582f850c45f0c3 Mon Sep 17 00:00:00 2001 From: PraneethS42 Date: Sat, 17 Oct 2026 23:30:46 -0400 Subject: [PATCH 4/4] Removed accidental package manager --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 61162c5..f056253 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,5 @@ "typescript": "^6.0.3", "webpack": "^5.111.1", "webpack-cli": "^7.2.3" - }, - "packageManager": "pnpm@12.6.0+sha512.3ef68f951cb111ac204b4a5a16f0b2ddf0da56a96e0413e81d855d9f0b55ef926714709028e1cd00c405c2c5fb7b9e8ec4dc46777c805d0373c2f2ff00fd20ec" + } }