From 310917fb7ca46c9f3c1fd84682ee279219f9e8fb Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Mon, 21 Feb 2022 08:55:00 +0100 Subject: [PATCH 01/12] Improve pythonPath config for Open-Needs --- vscode_ext/package.json | 5 +++ vscode_ext/src/extension.ts | 85 ++++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/vscode_ext/package.json b/vscode_ext/package.json index 749adb4..ea5ffc8 100644 --- a/vscode_ext/package.json +++ b/vscode_ext/package.json @@ -68,6 +68,11 @@ "type": "string", "default": "/build", "description": "Sphinx build directory. This is an absolute path!" + }, + "needls.pythonPath": { + "type": "string", + "default": "SystemDefaultPython", + "description": "Python path configured by the user. This is an absolute path!" } } } diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index b3f17bf..c4c55be 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -27,27 +27,67 @@ import { import { exec, ExecException } from 'child_process'; + let client: LanguageClient; +let log_prefix = "Extension Open-Needs: "; -async function getPythonPath(resource: Uri = null, outChannel: OutputChannel): Promise { - const extension = extensions.getExtension('ms-python.python'); - let pythonPath - if (extension) { - const usingNewInterpreterStorage = extension.packageJSON?.featureFlags?.usingNewInterpreterStorage; - if (usingNewInterpreterStorage) { - if (!extension.isActive) { - await extension.activate(); - } - pythonPath = extension.exports.settings.getExecutionDetails(resource).execCommand[0]; +async function getPythonPath(pythonPath:string, outChannel: OutputChannel): Promise { + // check pythonPath if empty, ask user to config; if not, use the pythonPath from workspace setting + pythonPath = await checkPythonPath(pythonPath, outChannel); + + const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath + pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) + + // check if pythonPath exists: run cmd to check python version to confirm + try { + await exec_py(pythonPath, outChannel, '--version'); + } catch (error) { + console.log(error) + outChannel.appendLine(error); + window.showInformationMessage(`${log_prefix} used python path ${pythonPath} not existed ${error}`); + + // ask user again until got valid python path + return getPythonPath("", outChannel) + } + + return pythonPath +} + +async function checkPythonPath(pythonPath: string, outChannel: OutputChannel): Promise { + // check if pythonPath empty + if (!pythonPath) { + console.log(`${log_prefix} Python path not configured yet!`); + window.showInformationMessage(`${log_prefix} Python path not configured yet!`); + + // System default python path + let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + + // remove line break + default_pythonPath = default_pythonPath.trim() + + // TODO: it's not stable somehow, window might not pop up, if other window pops up + // prompting window to ask user to config + window.showInformationMessage(`${log_prefix} please config python path`); + let user_input_pythonPath = await window.showInputBox({ + placeHolder: "Python path for Extension Open-Needs", + prompt: `${log_prefix} please specify python path`, + value: default_pythonPath, + ignoreFocusOut: true, + }); + + // use default python if no input from user + if (user_input_pythonPath) { + pythonPath = user_input_pythonPath + window.showInformationMessage(`${log_prefix} using specified python path ${user_input_pythonPath}`); } else { - pythonPath = workspace.getConfiguration('python', resource).get('pythonPath'); + pythonPath = default_pythonPath + window.showInformationMessage(`${log_prefix} using default system python path ${default_pythonPath}`); } - }else { - pythonPath = exec_py('python', outChannel, '-c', 'import sys; print(sys.executable)') + + console.log(`${log_prefix} Python path configured!`); } return pythonPath - } function exec_py(pythonPath: string, outChannel: OutputChannel, ...args: string[]): Promise { @@ -152,14 +192,15 @@ async function checkForNeedls(pythonPath: string, outChannel: OutputChannel, ext async function read_settings(_outChannel: OutputChannel) { let docs_root = workspace.getConfiguration('needls').get('docsRoot').toString(); let build_path = workspace.getConfiguration('needls').get('buildPath').toString(); + let pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath docs_root = docs_root.replace('${workspaceFolder}', currentWorkspaceFolderPath) build_path = build_path.replace('${workspaceFolder}', currentWorkspaceFolderPath) + pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) - - commands.executeCommand('needls.update_settings', docs_root, build_path); + commands.executeCommand('needls.update_settings', docs_root, build_path, pythonPath); } async function make_needs(pythonPath: string, outChannel: OutputChannel) { @@ -200,8 +241,7 @@ export async function activate(context: ExtensionContext): Promise { const packageFile = JSON.parse(fs.readFileSync(extensionPath, 'utf8')); const ext_version = packageFile.version; console.log(`Extension version: ${ext_version}`) - - + const disposable = commands.registerCommand('open-needs-ide.load', () => { // The code you place here will be executed every time your command is executed // Display a message box to the user @@ -218,7 +258,11 @@ export async function activate(context: ExtensionContext): Promise { outChannel.appendLine("CWD: " + cwd); const resource = window.activeTextEditor?.document.uri; - const pythonPath = await getPythonPath(resource, outChannel); + + // get pythonPath from workspace setting + let wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); + + const pythonPath = await getPythonPath(wk_pythonPath, outChannel); outChannel.appendLine("Python path: " + pythonPath); // Check for needls @@ -231,6 +275,9 @@ export async function activate(context: ExtensionContext): Promise { window.showErrorMessage("Python module needls not found! Needs extension can't start."); } + // update pythonPath for workspace setting + workspace.getConfiguration('needls').update('pythonPath', pythonPath, false); + // listen for changes of settings workspace.onDidChangeConfiguration( (_event) => { read_settings(outChannel); From 05cef3ceff54e28cfce7e910a30479e257e05c01 Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Mon, 21 Feb 2022 15:54:50 +0100 Subject: [PATCH 02/12] Fixed linting and updated docs --- docs/changelog.rst | 2 ++ docs/installation.rst | 1 + docs/settings.rst | 3 +++ test-envs/_basics/.vscode/settings.json | 3 ++- test-envs/setup_envs.py | 5 +++-- vscode_ext/src/extension.ts | 10 +++------- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c570e44..9fcad48 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,8 @@ Changelog **released**: under development +* Improvement: Supporting ``pythonPath`` config in settings of Open-Needs IDE. Default is system python path. :issue:`38` + 0.0.14 ------ diff --git a/docs/installation.rst b/docs/installation.rst index 0a806ae..fe9d589 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,6 +23,7 @@ Installation of the extension #. Update the **Build Path**. E.g. ``/home/my-user/my-project/docs/_build/need``. #. Update the **Docs Root**. E.g. ``/home/my-user/my-project/docs`` + #. Update the **pythonPath**. E.g. ``/home/my-user/my-project/.venv/bin/python``. Default is system python path. E.g. ``/usr/bin/python`. #. Open a reStructuredText file (`*.rst`) in workspace to trigger the activation of the extension. diff --git a/docs/settings.rst b/docs/settings.rst index c68a1df..01001c9 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -9,6 +9,8 @@ This extension contributes the following settings: expected. This file is created using using the `sphinx-needs builder `__ +:needls.pythonPath: Python path used to install `Open-Needs-IDE:needls`. Default system python path `/usr/bin/python` will be used if this setting is not configured. + Supported variables ------------------- **Open-Needs IDE** supports the usage of template variables, which get replaced during runtime. @@ -26,6 +28,7 @@ Inside a ``.vscode/settings.json`` file a configuration can look like:: { "needls.docsRoot": "${workspaceFolder}/docs" "needls.buildPath": "${workspaceFolder}/docs/_build/need", + "needls.pythonPath": "${workspaceFolder}/.venv/bin/python", } Settings menu diff --git a/test-envs/_basics/.vscode/settings.json b/test-envs/_basics/.vscode/settings.json index e283846..db66bc5 100644 --- a/test-envs/_basics/.vscode/settings.json +++ b/test-envs/_basics/.vscode/settings.json @@ -1,4 +1,5 @@ { "needls.buildPath": "{{build_path}}", - "needls.docsRoot": "{{docs_root}}" + "needls.docsRoot": "{{docs_root}}", + "needls.pythonPath": "{{pythonPath}}", } diff --git a/test-envs/setup_envs.py b/test-envs/setup_envs.py index 6d7d78b..ec6e435 100644 --- a/test-envs/setup_envs.py +++ b/test-envs/setup_envs.py @@ -23,9 +23,9 @@ def __init__(self, name: str, reuse: bool = False) -> None: self.test_envs_path = os.path.join(os.path.dirname(__file__)) self.basics_path = os.path.join(self.test_envs_path, "_basics") - + self.temp_env_path = os.path.join(self.test_envs_path, "../temp-envs") - + self.env_path = os.path.join(self.temp_env_path, name) def setup(self): @@ -75,6 +75,7 @@ def parse_templates(self): ".vscode/settings.json": { "build_path": "${workspaceFolder}/docs/_build", "docs_root": "${workspaceFolder}/docs", + "pythonPath": "${workspaceFolder}/.venv/bin/python", } } diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index c4c55be..b476d53 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -9,9 +9,7 @@ import * as fs from 'fs'; import { workspace, - extensions, commands, - Uri, ExtensionContext, window, OutputChannel, @@ -29,7 +27,7 @@ import { exec, ExecException } from 'child_process'; let client: LanguageClient; -let log_prefix = "Extension Open-Needs: "; +const log_prefix = "Extension Open-Needs: "; async function getPythonPath(pythonPath:string, outChannel: OutputChannel): Promise { // check pythonPath if empty, ask user to config; if not, use the pythonPath from workspace setting @@ -68,7 +66,7 @@ async function checkPythonPath(pythonPath: string, outChannel: OutputChannel): P // TODO: it's not stable somehow, window might not pop up, if other window pops up // prompting window to ask user to config window.showInformationMessage(`${log_prefix} please config python path`); - let user_input_pythonPath = await window.showInputBox({ + const user_input_pythonPath = await window.showInputBox({ placeHolder: "Python path for Extension Open-Needs", prompt: `${log_prefix} please specify python path`, value: default_pythonPath, @@ -257,10 +255,8 @@ export async function activate(context: ExtensionContext): Promise { const cwd = path.join(__dirname, "..", ".."); outChannel.appendLine("CWD: " + cwd); - const resource = window.activeTextEditor?.document.uri; - // get pythonPath from workspace setting - let wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); + const wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); const pythonPath = await getPythonPath(wk_pythonPath, outChannel); outChannel.appendLine("Python path: " + pythonPath); From 18245627cd16547a8d5168b0b3232fde83ea6bb9 Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Mon, 21 Feb 2022 16:39:41 +0100 Subject: [PATCH 03/12] Fixed inline literal --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index fe9d589..a0a9da7 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,7 +23,7 @@ Installation of the extension #. Update the **Build Path**. E.g. ``/home/my-user/my-project/docs/_build/need``. #. Update the **Docs Root**. E.g. ``/home/my-user/my-project/docs`` - #. Update the **pythonPath**. E.g. ``/home/my-user/my-project/.venv/bin/python``. Default is system python path. E.g. ``/usr/bin/python`. + #. Update the **pythonPath**. E.g. ``/home/my-user/my-project/.venv/bin/python``. Default is system python path. E.g. ``/usr/bin/python``. #. Open a reStructuredText file (`*.rst`) in workspace to trigger the activation of the extension. From d84a75b02222ebc9118fe8f89073721a7ba8a019 Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Tue, 22 Feb 2022 13:09:34 +0100 Subject: [PATCH 04/12] Added routine diagram for specify pythonPath --- docs/arch.rst | 33 +++++++++++++++++++++++++++++++++ docs/conf.py | 1 + docs/index.rst | 1 + vscode_ext/src/extension.ts | 8 +++----- 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 docs/arch.rst diff --git a/docs/arch.rst b/docs/arch.rst new file mode 100644 index 0000000..65a193c --- /dev/null +++ b/docs/arch.rst @@ -0,0 +1,33 @@ +Installation routine +==================== + +.. uml:: + + @startuml + start + + :Check needls.pythonPath specified in workspace setting; + + if (needls.pythonPath specified and valid?) then (no) + + #palegreen:repeat :Prompt inputbox to ask user to specify; + if (user specify?) then (yes) + :user specify python path or use default; + :press ENTER; + #yellow:check and validate given python path; + else (no) + #darkorange:press ESC; + end + + endif + + backward:Prompt inputbox again; + repeat while (valid?) is (no) + ->yes; + + else (yes) + + endif + + stop + @enduml diff --git a/docs/conf.py b/docs/conf.py index 0dd36c9..130c6fd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,6 +28,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + "sphinxcontrib.plantuml", "sphinxcontrib.needs", "sphinx_panels", "sphinx_issues", diff --git a/docs/index.rst b/docs/index.rst index 3052045..6ffdab0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -81,6 +81,7 @@ Content .. toctree:: :maxdepth: 2 + arch installation settings features diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index b476d53..499b011 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -73,16 +73,14 @@ async function checkPythonPath(pythonPath: string, outChannel: OutputChannel): P ignoreFocusOut: true, }); - // use default python if no input from user if (user_input_pythonPath) { pythonPath = user_input_pythonPath window.showInformationMessage(`${log_prefix} using specified python path ${user_input_pythonPath}`); + console.log(`${log_prefix} Python path configured!`); } else { - pythonPath = default_pythonPath - window.showInformationMessage(`${log_prefix} using default system python path ${default_pythonPath}`); + // User cancled to specify python path, stop activating extension + throw new Error(`${log_prefix} Python path not specified.`); } - - console.log(`${log_prefix} Python path configured!`); } return pythonPath From 773bed1c00966a38a3715ec532e1b17983f9a4bc Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Tue, 22 Feb 2022 14:42:13 +0100 Subject: [PATCH 05/12] Adapted diagram for needls install process --- docs/arch.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/arch.rst b/docs/arch.rst index 65a193c..43b428b 100644 --- a/docs/arch.rst +++ b/docs/arch.rst @@ -1,6 +1,8 @@ Installation routine ==================== +Process to install needls: + .. uml:: @startuml @@ -29,5 +31,18 @@ Installation routine endif + :Use specified valid pythonPath to install needls; + if (needls installed?) then (no) + #yellowgreen:install needls; + if (user confirm?) then (yes) + :needls install success; + else (no) + end + endif + + else (yes) + + endif + stop @enduml From f1966c3971c2950d6d52b0a466f38cbbba1e2864 Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Tue, 22 Feb 2022 15:56:45 +0100 Subject: [PATCH 06/12] Added trigger for diagram --- docs/arch.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/arch.rst b/docs/arch.rst index 43b428b..0eb0dc2 100644 --- a/docs/arch.rst +++ b/docs/arch.rst @@ -8,6 +8,8 @@ Process to install needls: @startuml start + :Trigger\n 1. open-needs-ide.load\n 2. open .rst file; + :Check needls.pythonPath specified in workspace setting; if (needls.pythonPath specified and valid?) then (no) @@ -23,7 +25,6 @@ Process to install needls: endif - backward:Prompt inputbox again; repeat while (valid?) is (no) ->yes; From ccb3bc6ad1a02ad4acfe1f112778896d2f3d5e29 Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Wed, 23 Feb 2022 10:15:02 +0100 Subject: [PATCH 07/12] Adated pythonPAth func --- vscode_ext/package.json | 1 - vscode_ext/src/extension.ts | 72 +++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/vscode_ext/package.json b/vscode_ext/package.json index ea5ffc8..58b2fcc 100644 --- a/vscode_ext/package.json +++ b/vscode_ext/package.json @@ -71,7 +71,6 @@ }, "needls.pythonPath": { "type": "string", - "default": "SystemDefaultPython", "description": "Python path configured by the user. This is an absolute path!" } } diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index 499b011..8c7a866 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -29,9 +29,16 @@ import { exec, ExecException } from 'child_process'; let client: LanguageClient; const log_prefix = "Extension Open-Needs: "; -async function getPythonPath(pythonPath:string, outChannel: OutputChannel): Promise { +async function checkAndValidatePythonPath(pythonPath:string, outChannel: OutputChannel): Promise { // check pythonPath if empty, ask user to config; if not, use the pythonPath from workspace setting - pythonPath = await checkPythonPath(pythonPath, outChannel); + if (!pythonPath) { + pythonPath = await getUserInputPythonPath(pythonPath, outChannel); + + // user does not specify python path + if (pythonPath === undefined) { + return pythonPath + } + } const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) @@ -45,42 +52,39 @@ async function getPythonPath(pythonPath:string, outChannel: OutputChannel): Prom window.showInformationMessage(`${log_prefix} used python path ${pythonPath} not existed ${error}`); // ask user again until got valid python path - return getPythonPath("", outChannel) + return checkAndValidatePythonPath("", outChannel) } return pythonPath } -async function checkPythonPath(pythonPath: string, outChannel: OutputChannel): Promise { - // check if pythonPath empty - if (!pythonPath) { - console.log(`${log_prefix} Python path not configured yet!`); - window.showInformationMessage(`${log_prefix} Python path not configured yet!`); - - // System default python path - let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); - - // remove line break - default_pythonPath = default_pythonPath.trim() - - // TODO: it's not stable somehow, window might not pop up, if other window pops up - // prompting window to ask user to config - window.showInformationMessage(`${log_prefix} please config python path`); - const user_input_pythonPath = await window.showInputBox({ - placeHolder: "Python path for Extension Open-Needs", - prompt: `${log_prefix} please specify python path`, - value: default_pythonPath, - ignoreFocusOut: true, - }); - - if (user_input_pythonPath) { - pythonPath = user_input_pythonPath - window.showInformationMessage(`${log_prefix} using specified python path ${user_input_pythonPath}`); - console.log(`${log_prefix} Python path configured!`); - } else { - // User cancled to specify python path, stop activating extension - throw new Error(`${log_prefix} Python path not specified.`); - } +async function getUserInputPythonPath(pythonPath: string, outChannel: OutputChannel): Promise { + console.log(`${log_prefix} Python path not configured yet!`); + window.showInformationMessage(`${log_prefix} please specify python path.`); + + // System default python path + let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + + // remove line break + default_pythonPath = default_pythonPath.trim() + + // TODO: it's not stable somehow, window might not pop up, if other window pops up + // prompting window to ask user to config + const user_input_pythonPath = await window.showInputBox({ + placeHolder: "Python path for Extension Open-Needs", + prompt: "Example: ${workspaceFolder}/.venv/bin/python", + value: default_pythonPath, + ignoreFocusOut: true, + }); + + if (user_input_pythonPath) { + pythonPath = user_input_pythonPath + window.showInformationMessage(`${log_prefix} using specified python path ${user_input_pythonPath}`); + console.log(`${log_prefix} Python path configured!`); + } else { + // User cancled to specify python path, stop activating extension + // throw new Error(`${log_prefix} Python path not specified.`); + pythonPath = undefined } return pythonPath @@ -256,7 +260,7 @@ export async function activate(context: ExtensionContext): Promise { // get pythonPath from workspace setting const wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); - const pythonPath = await getPythonPath(wk_pythonPath, outChannel); + const pythonPath = await checkAndValidatePythonPath(wk_pythonPath, outChannel); outChannel.appendLine("Python path: " + pythonPath); // Check for needls From 24be568f5aba6f98c49ff7ebc3fafcc02cebbe5d Mon Sep 17 00:00:00 2001 From: Daniel Woste Date: Wed, 23 Feb 2022 11:30:21 +0100 Subject: [PATCH 08/12] restructuring active() and co. --- .vscode/extensions.json | 6 + test-envs/_basics/.vscode/settings.json | 1 - vscode_ext/src/extension.ts | 251 ++++++++++++------------ 3 files changed, 134 insertions(+), 124 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..551484b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-python.vscode-pylance", + "ms-python.python" + ] +} \ No newline at end of file diff --git a/test-envs/_basics/.vscode/settings.json b/test-envs/_basics/.vscode/settings.json index db66bc5..e3083b6 100644 --- a/test-envs/_basics/.vscode/settings.json +++ b/test-envs/_basics/.vscode/settings.json @@ -1,5 +1,4 @@ { "needls.buildPath": "{{build_path}}", "needls.docsRoot": "{{docs_root}}", - "needls.pythonPath": "{{pythonPath}}", } diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index 8c7a866..672a16d 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -29,66 +29,6 @@ import { exec, ExecException } from 'child_process'; let client: LanguageClient; const log_prefix = "Extension Open-Needs: "; -async function checkAndValidatePythonPath(pythonPath:string, outChannel: OutputChannel): Promise { - // check pythonPath if empty, ask user to config; if not, use the pythonPath from workspace setting - if (!pythonPath) { - pythonPath = await getUserInputPythonPath(pythonPath, outChannel); - - // user does not specify python path - if (pythonPath === undefined) { - return pythonPath - } - } - - const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath - pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) - - // check if pythonPath exists: run cmd to check python version to confirm - try { - await exec_py(pythonPath, outChannel, '--version'); - } catch (error) { - console.log(error) - outChannel.appendLine(error); - window.showInformationMessage(`${log_prefix} used python path ${pythonPath} not existed ${error}`); - - // ask user again until got valid python path - return checkAndValidatePythonPath("", outChannel) - } - - return pythonPath -} - -async function getUserInputPythonPath(pythonPath: string, outChannel: OutputChannel): Promise { - console.log(`${log_prefix} Python path not configured yet!`); - window.showInformationMessage(`${log_prefix} please specify python path.`); - - // System default python path - let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); - - // remove line break - default_pythonPath = default_pythonPath.trim() - - // TODO: it's not stable somehow, window might not pop up, if other window pops up - // prompting window to ask user to config - const user_input_pythonPath = await window.showInputBox({ - placeHolder: "Python path for Extension Open-Needs", - prompt: "Example: ${workspaceFolder}/.venv/bin/python", - value: default_pythonPath, - ignoreFocusOut: true, - }); - - if (user_input_pythonPath) { - pythonPath = user_input_pythonPath - window.showInformationMessage(`${log_prefix} using specified python path ${user_input_pythonPath}`); - console.log(`${log_prefix} Python path configured!`); - } else { - // User cancled to specify python path, stop activating extension - // throw new Error(`${log_prefix} Python path not specified.`); - pythonPath = undefined - } - - return pythonPath -} function exec_py(pythonPath: string, outChannel: OutputChannel, ...args: string[]): Promise { const cmd = [pythonPath, ...args]; @@ -116,54 +56,76 @@ function exec_py(pythonPath: string, outChannel: OutputChannel, ...args: string[ }); } -async function installNeedls(pythonPath: string, outChannel: OutputChannel, version: string): Promise { - const install = await window.showInformationMessage( - `Install needls==${version} from PyPI?`, - 'Yes', - 'No' - ).then( (item) => { - if ( item === 'Yes' ) { - return true; - } else { - return false - } - }); - if (install === true) { - try { - await exec_py( - pythonPath, - outChannel, - '-m', - 'pip', - 'install', - 'pip', - '--upgrade' - ); - await exec_py( - pythonPath, - outChannel, - '-m', - 'pip', - 'uninstall', - 'open-needs-ls', - '-y' - ); - await exec_py( - pythonPath, - outChannel, - '-m', - 'pip', - 'install', - `open-needs-ls==${version}` - ); - window.showInformationMessage("Needls successfully installed."); - return true; - } catch (e){ - console.log(e) - window.showInformationMessage(`Needls could not be installed ${e}`); - } +async function checkAndValidatePythonPath(pythonPath:string, outChannel: OutputChannel): Promise { + // check pythonPath if empty, ask user to config; if not, use the pythonPath from workspace setting + if (!pythonPath) { + return false } - return false + // check if pythonPath exists: run cmd to check python version to confirm + try { + await exec_py(pythonPath, outChannel, '--version'); + } catch (error) { + console.log(error) + outChannel.appendLine(error); + + return false + } + return true +} + +async function getUserInputPythonPath(pythonPathProposal: string, outChannel: OutputChannel): Promise { + // ToDo: Ask user for pythonPath, check via checkAndValidatePythonPath(pythonPath), if not ask again --> Return valid pathon path + window.showInformationMessage(`${log_prefix} please specify python path.`); + + let pythonPath = "" + while(await checkAndValidatePythonPath(pythonPath, outChannel) == false) { + // prompting window to ask user to config + const user_input_pythonPath = await window.showInputBox({ + placeHolder: "Python path for Extension Open-Needs", + prompt: "Example: ${workspaceFolder}/.venv/bin/python", + value: pythonPathProposal, + ignoreFocusOut: true, + }); + + if (user_input_pythonPath) { + pythonPath = user_input_pythonPath + const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath + pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) + } + } + + return pythonPath +} + +async function installNeedls(pythonPath: string, outChannel: OutputChannel, version: string): Promise { + await exec_py( + pythonPath, + outChannel, + '-m', + 'pip', + 'install', + 'pip', + '--upgrade' + ); + await exec_py( + pythonPath, + outChannel, + '-m', + 'pip', + 'uninstall', + 'open-needs-ls', + '-y' + ); + await exec_py( + pythonPath, + outChannel, + '-m', + 'pip', + 'install', + `open-needs-ls==${version}` + ); + window.showInformationMessage("Needls successfully installed."); + return true; } async function checkForNeedls(pythonPath: string, outChannel: OutputChannel, ext_version: string): Promise { @@ -176,17 +138,14 @@ async function checkForNeedls(pythonPath: string, outChannel: OutputChannel, ext ); needls_version = needls_version.trim(); if (ext_version != needls_version) { - window.showWarningMessage(`Needls found but wrong version: ${needls_version}\nVersion needed: ${ext_version}. - Needls should be reinstalled`); return false; } - return true; } catch (e) { console.warn(e) outChannel.appendLine(e); - window.showWarningMessage(`Error during detecting needls. Needls should be reinstalled.`); return false } + return true; } async function read_settings(_outChannel: OutputChannel) { @@ -251,30 +210,78 @@ export async function activate(context: ExtensionContext): Promise { }); context.subscriptions.push(disposable); - //Create output channel for logging + //Create oversionutput channel for logging const outChannel = window.createOutputChannel("Open-Needs IDE"); const cwd = path.join(__dirname, "..", ".."); outChannel.appendLine("CWD: " + cwd); + + // + // PYTHON PATH AND USER CONF HANDLING + // + // get pythonPath from workspace setting - const wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); + let wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); + const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath + wk_pythonPath = wk_pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) + + let pythonPath = "" + if (wk_pythonPath == "") { + const sysPythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + pythonPath = await getUserInputPythonPath(sysPythonPath, outChannel); + }else if (!checkAndValidatePythonPath(wk_pythonPath, outChannel)){ + pythonPath = await getUserInputPythonPath(wk_pythonPath, outChannel); + }else{ + pythonPath = wk_pythonPath + } - const pythonPath = await checkAndValidatePythonPath(wk_pythonPath, outChannel); outChannel.appendLine("Python path: " + pythonPath); + if (pythonPath === undefined) { + window.showErrorMessage("Python path not given. Extension can not be laoded."); + return + } + + // update pythonPath for workspace setting + workspace.getConfiguration('needls').update('pythonPath', pythonPath, false); + + // Check for needls let needls_installed = await checkForNeedls(pythonPath, outChannel, ext_version); + + // Ask for needls installation if ( !needls_installed ) { - needls_installed = await installNeedls(pythonPath, outChannel, ext_version); + window.showWarningMessage("Invalid Open-Needs Server installation. Please reinstall..."); + const install = await window.showInformationMessage( + `Install needls==${ext_version} from PyPI?`, + 'Yes', + 'No' + ).then( (item) => { + if ( item === 'Yes' ) { + return true; + } else { + return false + } + }); + if (install) { + try { + needls_installed = await installNeedls(pythonPath, outChannel, ext_version); + } catch (e){ + console.log(e) + window.showInformationMessage(`Needls could not be installed. Error: ${e}`); + return + } + } } if ( !needls_installed ) { window.showErrorMessage("Python module needls not found! Needs extension can't start."); } - // update pythonPath for workspace setting - workspace.getConfiguration('needls').update('pythonPath', pythonPath, false); + //} + // REGISTER INTERNAL HANDLERS + // // listen for changes of settings workspace.onDidChangeConfiguration( (_event) => { @@ -303,7 +310,7 @@ export async function activate(context: ExtensionContext): Promise { options: { cwd: cwd } } as Executable }; - + // let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for plain text documents @@ -317,7 +324,7 @@ export async function activate(context: ExtensionContext): Promise { } }; - if (pythonPath) { + // Create the language client and start the client. client = new LanguageClient( 'open-needs-ls', @@ -333,10 +340,7 @@ export async function activate(context: ExtensionContext): Promise { client.onReady().then(async () => { await read_settings(outChannel); }) - } else { - window.showErrorMessage("Python not found! Can't activate extension."); - outChannel.appendLine("Python not found! Can't activate extension."); - } + } export function deactivate(): Thenable | undefined { @@ -345,3 +349,4 @@ export function deactivate(): Thenable | undefined { } return client.stop(); } + From 851b94cff9445702436ea3d1d71dd0ec880996af Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Wed, 23 Feb 2022 14:10:02 +0100 Subject: [PATCH 09/12] Support user ESC and update input proposol --- vscode_ext/src/extension.ts | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index 672a16d..641ce20 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -74,7 +74,6 @@ async function checkAndValidatePythonPath(pythonPath:string, outChannel: OutputC } async function getUserInputPythonPath(pythonPathProposal: string, outChannel: OutputChannel): Promise { - // ToDo: Ask user for pythonPath, check via checkAndValidatePythonPath(pythonPath), if not ask again --> Return valid pathon path window.showInformationMessage(`${log_prefix} please specify python path.`); let pythonPath = "" @@ -89,9 +88,17 @@ async function getUserInputPythonPath(pythonPathProposal: string, outChannel: Ou if (user_input_pythonPath) { pythonPath = user_input_pythonPath + + // update pythonPathProposal for inputbox + pythonPathProposal = pythonPath + const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath pythonPath = pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) - } + } else { + // user canceled + pythonPath = undefined + break; + } } return pythonPath @@ -216,7 +223,6 @@ export async function activate(context: ExtensionContext): Promise { const cwd = path.join(__dirname, "..", ".."); outChannel.appendLine("CWD: " + cwd); - // // PYTHON PATH AND USER CONF HANDLING // @@ -225,10 +231,11 @@ export async function activate(context: ExtensionContext): Promise { let wk_pythonPath = workspace.getConfiguration('needls').get('pythonPath').toString(); const currentWorkspaceFolderPath = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)?.uri.fsPath wk_pythonPath = wk_pythonPath.replace('${workspaceFolder}', currentWorkspaceFolderPath) - + let pythonPath = "" if (wk_pythonPath == "") { - const sysPythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + let sysPythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + sysPythonPath = sysPythonPath.trim(); pythonPath = await getUserInputPythonPath(sysPythonPath, outChannel); }else if (!checkAndValidatePythonPath(wk_pythonPath, outChannel)){ pythonPath = await getUserInputPythonPath(wk_pythonPath, outChannel); @@ -236,20 +243,19 @@ export async function activate(context: ExtensionContext): Promise { pythonPath = wk_pythonPath } - outChannel.appendLine("Python path: " + pythonPath); - if (pythonPath === undefined) { - window.showErrorMessage("Python path not given. Extension can not be laoded."); + window.showErrorMessage(`Python path not given. ${log_prefix} can not be loaded.`); return } + outChannel.appendLine("Python path: " + pythonPath); + // update pythonPath for workspace setting workspace.getConfiguration('needls').update('pythonPath', pythonPath, false); - // Check for needls let needls_installed = await checkForNeedls(pythonPath, outChannel, ext_version); - + // Ask for needls installation if ( !needls_installed ) { window.showWarningMessage("Invalid Open-Needs Server installation. Please reinstall..."); @@ -279,7 +285,7 @@ export async function activate(context: ExtensionContext): Promise { window.showErrorMessage("Python module needls not found! Needs extension can't start."); } - //} + // // REGISTER INTERNAL HANDLERS // @@ -310,7 +316,7 @@ export async function activate(context: ExtensionContext): Promise { options: { cwd: cwd } } as Executable }; - // let default_pythonPath = await exec_py('python', outChannel, '-c', '"import sys; print(sys.executable)"'); + // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for plain text documents @@ -324,23 +330,21 @@ export async function activate(context: ExtensionContext): Promise { } }; - - // Create the language client and start the client. - client = new LanguageClient( - 'open-needs-ls', - 'Open-Needs LS', - serverOptions, - clientOptions - ); + // Create the language client and start the client. + client = new LanguageClient( + 'open-needs-ls', + 'Open-Needs LS', + serverOptions, + clientOptions + ); - // Start the client. This will also launch the server - client.start(); + // Start the client. This will also launch the server + client.start(); - // set doc root and needs.json file - client.onReady().then(async () => { - await read_settings(outChannel); - }) - + // set doc root and needs.json file + client.onReady().then(async () => { + await read_settings(outChannel); + }) } export function deactivate(): Thenable | undefined { From a05c8da3b24b600b47aabb0b5eedf594f0bf95ae Mon Sep 17 00:00:00 2001 From: haiyangToAI Date: Wed, 23 Feb 2022 14:11:21 +0100 Subject: [PATCH 10/12] Added test temp env for empty pythonPath --- .vscode/launch.json | 12 ++++++++++++ test-envs/_basics/.vscode/settings.json | 1 + test-envs/setup_envs.py | 5 ++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 0916fd7..3c9821f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -34,6 +34,18 @@ "outFiles": ["${workspaceFolder}/out/test/**/*.js"], "preLaunchTask": "${defaultBuildTask}" }, + { + "name": "Launch Extension Dummy", + "type": "extensionHost", + "request": "launch", + "args": [ + //"--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}/vscode_ext", + "${workspaceFolder}/temp-envs/project_dummy_needls_test", + ], + "outFiles": ["${workspaceFolder}/out/test_dummy/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, { "name": "Setup Envs", "type": "python", diff --git a/test-envs/_basics/.vscode/settings.json b/test-envs/_basics/.vscode/settings.json index e3083b6..db66bc5 100644 --- a/test-envs/_basics/.vscode/settings.json +++ b/test-envs/_basics/.vscode/settings.json @@ -1,4 +1,5 @@ { "needls.buildPath": "{{build_path}}", "needls.docsRoot": "{{docs_root}}", + "needls.pythonPath": "{{pythonPath}}", } diff --git a/test-envs/setup_envs.py b/test-envs/setup_envs.py index ec6e435..f42fdca 100644 --- a/test-envs/setup_envs.py +++ b/test-envs/setup_envs.py @@ -75,7 +75,7 @@ def parse_templates(self): ".vscode/settings.json": { "build_path": "${workspaceFolder}/docs/_build", "docs_root": "${workspaceFolder}/docs", - "pythonPath": "${workspaceFolder}/.venv/bin/python", + "pythonPath": "", } } @@ -101,6 +101,9 @@ def start(): project1 = ProjectEnv("project_no_needls", reuse=True) project1.setup() + project2 = ProjectEnv("project_dummy_needls_test", reuse=True) + project2.setup() + if "main" in __name__: start() From 445abeff1959fca858b9b8419f14cde7e9d34138 Mon Sep 17 00:00:00 2001 From: Daniel Woste Date: Wed, 23 Feb 2022 16:20:03 +0100 Subject: [PATCH 11/12] more checks for needls --- vscode_ext/src/extension.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vscode_ext/src/extension.ts b/vscode_ext/src/extension.ts index 641ce20..287ea0e 100644 --- a/vscode_ext/src/extension.ts +++ b/vscode_ext/src/extension.ts @@ -152,6 +152,21 @@ async function checkForNeedls(pythonPath: string, outChannel: OutputChannel, ext outChannel.appendLine(e); return false } + + + try { + await exec_py( + pythonPath, + outChannel, + '-c', + '"import needls.server"' + ); + } catch (e) { + console.warn(e) + outChannel.appendLine(e); + return false + } + return true; } From f25fea42ac9e8468226d4ffba904e6961dfc70b4 Mon Sep 17 00:00:00 2001 From: Daniel Woste Date: Wed, 23 Feb 2022 16:35:38 +0100 Subject: [PATCH 12/12] version raise to 0.0.15 --- docs/changelog.rst | 8 +++++++- needls/version.py | 2 +- setup.py | 1 + vscode_ext/package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9fcad48..fae050f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,12 +1,18 @@ Changelog ========= -0.0.15 +0.0.16 ------ **released**: under development +0.0.15 +------ + +**released**: 23.02.2022 + * Improvement: Supporting ``pythonPath`` config in settings of Open-Needs IDE. Default is system python path. :issue:`38` +* Bugfix: Mutliple little changes 0.0.14 diff --git a/needls/version.py b/needls/version.py index 311f216..6561790 100644 --- a/needls/version.py +++ b/needls/version.py @@ -1 +1 @@ -__version__ = "0.0.14" +__version__ = "0.0.15" diff --git a/setup.py b/setup.py index 75496b1..33eb4c3 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ setup( name="open-needs-ls", + # Don't forget package.json, changelog and needls.version version=main_ns["__version__"], url="https://open-needs.org", author="Daniel Woste", diff --git a/vscode_ext/package.json b/vscode_ext/package.json index 58b2fcc..e823f91 100644 --- a/vscode_ext/package.json +++ b/vscode_ext/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/open-needs/open-needs-ide.git" }, "homepage": "https://open-needs.org/open-needs-ide/", - "version": "0.0.14", + "version": "0.0.15", "engines": { "vscode": "^1.52.0" },