diff --git a/bundled/tool/install_dependencies.py b/bundled/tool/install_dependencies.py new file mode 100644 index 0000000..114d8ef --- /dev/null +++ b/bundled/tool/install_dependencies.py @@ -0,0 +1,42 @@ +import subprocess +import sys +from pathlib import Path + +def install_dependencies(extension_root_dir): + """ + Install dependencies required for the Kedro extension. + + Args: + extension_root_dir (str): The root directory of the extension. + Raises: + ImportError: If the required dependencies are not found. + """ + ... + libs_path = Path(extension_root_dir) / "bundled" / "libs" + requirements_path = Path(extension_root_dir) / "kedro-viz-requirements.txt" + + try: + import fastapi + import orjson + except ImportError: + subprocess.check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "-r", + Path(requirements_path), + "-t", + Path(libs_path), + "--no-cache-dir", + ] + ) + + +if __name__ == "__main__": + if len(sys.argv) > 1: + extension_root_dir = sys.argv[1] + else: + extension_root_dir = None + install_dependencies(extension_root_dir) diff --git a/kedro-viz-requirements.txt b/kedro-viz-requirements.txt new file mode 100644 index 0000000..3a5fac9 --- /dev/null +++ b/kedro-viz-requirements.txt @@ -0,0 +1,3 @@ +fastapi>=0.100.0,<0.200.0 +pydantic>=2.0.0 # In case of FastAPI installs pydantic==1 +orjson>=3.9, <4.0 \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index a8b2295..4fabe11 100644 --- a/noxfile.py +++ b/noxfile.py @@ -24,6 +24,18 @@ def _install_bundle(session: nox.Session) -> None: "./requirements.txt", ) + # Installing kedro-viz to bundled/libs from here and not keeping it in requirements.in to avoid installing all its required packages + session.install( + "-t", + "./bundled/libs", + "--no-cache-dir", + "--implementation", + "py", + "--no-deps", + "--upgrade", + "kedro-viz", + ) + def _check_files(names: List[str]) -> None: root_dir = pathlib.Path(__file__).parent diff --git a/package-lock.json b/package-lock.json index 547cbd3..4f5214a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "Kedro", - "version": "0.1.0", + "version": "0.2.0-rc0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Kedro", - "version": "0.1.0", + "version": "0.2.0-rc0", "license": "Apache2", "dependencies": { "@vscode/python-extension": "^1.0.5", diff --git a/requirements.in b/requirements.in index 978e9d4..57704ec 100644 --- a/requirements.in +++ b/requirements.in @@ -11,5 +11,7 @@ # Required packages pygls packaging +networkx>=2.5 +sqlalchemy>=1.4, <3 # TODO: Add your tool here diff --git a/requirements.txt b/requirements.txt index fee6f80..e3a1714 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,20 +1,30 @@ # -# This file is autogenerated by pip-compile with Python 3.12 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile ./requirements.in # -attrs==23.2.0 +attrs==24.2.0 # via # cattrs # lsprotocol -cattrs==23.2.3 +cattrs==24.1.0 # via # lsprotocol # pygls +exceptiongroup==1.2.2 + # via cattrs lsprotocol==2023.0.1 # via pygls +networkx==3.3 + # via -r ./requirements.in packaging==24.1 # via -r ./requirements.in pygls==1.3.1 # via -r ./requirements.in +sqlalchemy==2.0.32 + # via -r ./requirements.in +typing-extensions==4.12.2 + # via + # cattrs + # sqlalchemy diff --git a/src/common/callPythonScript.ts b/src/common/callPythonScript.ts new file mode 100644 index 0000000..2455fbf --- /dev/null +++ b/src/common/callPythonScript.ts @@ -0,0 +1,28 @@ +import * as cp from 'child_process'; +import { getInterpreterDetails } from './python'; + +/** + * Calls a Python script with the specified arguments. + * + * @param pathToScript - The path to the Python script. + * @param scriptArgv - The arguments to pass to the script. + * @param context - The context object. + * @returns A promise that resolves when the script execution is complete. + */ +export async function callPythonScript(pathToScript: string, scriptArgv: string, context: any): Promise { + return new Promise(async (resolve, reject) => { + const script = context.asAbsolutePath(pathToScript); + const interpreterDetails = await getInterpreterDetails(); + const pythonPath = interpreterDetails['path'] && interpreterDetails['path'][0]; + + cp.exec(`${pythonPath} ${script} ${scriptArgv}`, (error, stdout, stderr) => { + if (error) { + console.error(stderr); + reject(error); + } else { + console.log(stdout); + resolve(stdout); + } + }); + }); +} diff --git a/src/common/utilities.ts b/src/common/utilities.ts index ec6b6c4..66b84a8 100644 --- a/src/common/utilities.ts +++ b/src/common/utilities.ts @@ -3,10 +3,13 @@ import * as fs from 'fs-extra'; import * as path from 'path'; +import * as vscode from 'vscode'; import { LogLevel, Uri, WorkspaceFolder } from 'vscode'; import { Trace } from 'vscode-jsonrpc/node'; import { getWorkspaceFolders } from './vscodeapi'; -import KedroVizPanel from '../webview/vizWebView'; +import { callPythonScript } from './callPythonScript'; +import { EXTENSION_ROOT_DIR } from './constants'; +import { traceError, traceLog } from './log/logging'; function logLevelToTrace(logLevel: LogLevel): Trace { switch (logLevel) { @@ -66,3 +69,24 @@ export async function getProjectRoot(): Promise { return rootWorkspace; } } + +export async function installDependenciesIfNeeded(context: vscode.ExtensionContext): Promise { + const alreadyInstalled = context.globalState.get('dependenciesInstalled', false); + + if (!alreadyInstalled) { + const pathToScript = 'bundled/tool/install_dependencies.py'; + try { + const stdout = await callPythonScript(pathToScript, EXTENSION_ROOT_DIR, context); + + // Check if the script output contains the success message + if (stdout.includes('Successfully installed')) { + context.globalState.update('dependenciesInstalled', true); + traceLog(`Python dependencies installed!`); + console.log('Python dependencies installed!'); + } + } catch (error) { + traceError(`Failed to install Python dependencies:: ${error}`); + console.error(`Failed to install Python dependencies:: ${error}`); + } + } +} diff --git a/src/extension.ts b/src/extension.ts index 75eaf5e..b6285f5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,17 +21,15 @@ import { restartServer } from './common/server'; import { checkIfConfigurationChanged, getInterpreterFromSetting } from './common/settings'; import { loadServerDefaults } from './common/setup'; import { createStatusBar } from './common/status_bar'; -import { getLSClientTraceLevel } from './common/utilities'; +import { getLSClientTraceLevel, installDependenciesIfNeeded } from './common/utilities'; import { createOutputChannel, onDidChangeConfiguration, registerCommand } from './common/vscodeapi'; import KedroVizPanel from './webview/vizWebView'; let lsClient: LanguageClient | undefined; -export async function getlsClient() { - return lsClient; -} - export async function activate(context: vscode.ExtensionContext): Promise { + await installDependenciesIfNeeded(context); + // This is required to get server name and module. This should be // the first thing that we do in this extension. const serverInfo = loadServerDefaults(); @@ -46,6 +44,7 @@ export async function activate(context: vscode.ExtensionContext): Promise // List of commands const CMD_RESTART_SERVER = `${serverId}.restart`; const CMD_SELECT_ENV = `${serverId}.selectEnvironment`; + const CMD_RUN_KEDRO_VIZ = `${serverId}.runKedroViz`; // Status Bar const statusBarItem = await createStatusBar(CMD_SELECT_ENV, serverId); @@ -70,7 +69,7 @@ export async function activate(context: vscode.ExtensionContext): Promise ); context.subscriptions.push( - vscode.commands.registerCommand('kedro.runKedroViz', async () => { + registerCommand(CMD_RUN_KEDRO_VIZ, async () => { KedroVizPanel.createOrShow(context.extensionUri); const projectData = await executeGetProjectDataCommand(lsClient); KedroVizPanel.currentPanel?.updateData(projectData); @@ -141,9 +140,6 @@ export async function activate(context: vscode.ExtensionContext): Promise registerCommand('kedro.sendDefinitionRequest', async (word) => { await executeServerDefinitionCommand(lsClient, word); }), - vscode.commands.registerCommand('kedro.runKedroViz', () => { - KedroVizPanel.createOrShow(context.extensionUri); - }), ); setImmediate(async () => { diff --git a/src/test/python_tests/requirements.txt b/src/test/python_tests/requirements.txt index eb572ae..4987d28 100644 --- a/src/test/python_tests/requirements.txt +++ b/src/test/python_tests/requirements.txt @@ -1,9 +1,11 @@ # -# This file is autogenerated by pip-compile with Python 3.12 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile ./src/test/python_tests/requirements.in # +exceptiongroup==1.2.2 + # via pytest iniconfig==2.0.0 # via pytest packaging==24.1 @@ -11,10 +13,12 @@ packaging==24.1 pluggy==1.5.0 # via pytest pyhamcrest==2.1.0 - # via -r requirements.in + # via -r ./src/test/python_tests/requirements.in pytest==8.3.2 - # via -r requirements.in + # via -r ./src/test/python_tests/requirements.in python-jsonrpc-server==0.4.0 - # via -r requirements.in + # via -r ./src/test/python_tests/requirements.in +tomli==2.0.1 + # via pytest ujson==5.10.0 # via python-jsonrpc-server diff --git a/src/webview/vizWebView.ts b/src/webview/vizWebView.ts index 84f5409..6097453 100644 --- a/src/webview/vizWebView.ts +++ b/src/webview/vizWebView.ts @@ -69,8 +69,7 @@ export default class KedroVizPanel { switch (message.command) { case 'fromWebview': if (message.node.type === 'data') { - // await executeServerDefinitionCommand(getlsClient(), message.node.text); - await vscode.commands.executeCommand('kedro.sendDefinitionRequest', message.node.text) + await vscode.commands.executeCommand('kedro.sendDefinitionRequest', message.node.text); } else { await goToDefinition(message.node); } diff --git a/webview/package-lock.json b/webview/package-lock.json index c628fa8..f67deca 100644 --- a/webview/package-lock.json +++ b/webview/package-lock.json @@ -1,14 +1,14 @@ { - "name": "kedro-viz-standalone", - "version": "0.1.0", + "name": "kedro-viz-Webview", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "kedro-viz-standalone", - "version": "0.1.0", + "name": "kedro-viz-Webview", + "version": "0.0.1", "dependencies": { - "@quantumblack/kedro-viz": "^9.2.0", + "my-kviz": "^0.0.1-alpha.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1" @@ -4468,64 +4468,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@quantumblack/kedro-viz": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@quantumblack/kedro-viz/-/kedro-viz-9.2.0.tgz", - "integrity": "sha512-Gn2CojSDO1d1KL48scy6iEgeSks14X0lMJQc01lsAzPCNNGrwN+iZp6q6t7PwXBCmCM7BiLvUwXWZdFB0hQR/A==", - "dependencies": { - "@apollo/client": "^3.5.6", - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", - "@graphql-tools/schema": "7.1.5", - "@mui/icons-material": "^5.11.9", - "@mui/material": "^5.11.10", - "@mui/system": "^5.14.18", - "@mui/x-tree-view": "^6.17.0", - "batching-toposort": "^1.2.0", - "classnames": "^2.3.1", - "d3": "^7.6.1", - "d3-fetch": "^2.0.0", - "d3-interpolate": "^2.0.1", - "d3-interpolate-path": "^2.2.3", - "d3-scale": "^3.3.0", - "d3-selection": "^2.0.0", - "d3-shape": "^2.1.0", - "d3-transition": "^2.0.0", - "d3-zoom": "^2.0.0", - "dayjs": "^1.10.7", - "deepmerge": "^4.2.2", - "fetch-mock": "^9.11.0", - "fishery": "^1.4.0", - "graphql": "^15.8.0", - "graphql-type-json": "^0.3.2", - "highlight.js": "^10.7.3", - "kiwi.js": "^1.1.3", - "lodash": "^4.17.21", - "plotly.js-dist-min": "^2.26.0", - "react-content-loader": "^6.2.0", - "react-csv": "^2.2.2", - "react-custom-scrollbars-2": "^4.5.0", - "react-json-view": "^1.21.3", - "react-plotly.js": "^2.5.1", - "react-redux": "^8.1.3", - "react-router": "^5.2.1", - "react-router-dom": "^5.3.0", - "redux": "^4.1.2", - "redux-mock-store": "^1.5.4", - "redux-thunk": "^2.4.1", - "redux-watch": "^1.2.0", - "reselect": "^4.1.5", - "seedrandom": "^3.0.5", - "sinon": "^12.0.1", - "svg-crowbar": "^0.6.5", - "uuid": "^9.0.0", - "what-input": "^5.2.10" - }, - "peerDependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.19.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", @@ -15680,6 +15622,64 @@ "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", "peer": true }, + "node_modules/my-kviz": { + "version": "0.0.1-alpha.2", + "resolved": "https://registry.npmjs.org/my-kviz/-/my-kviz-0.0.1-alpha.2.tgz", + "integrity": "sha512-hi9h5crQt+Zhtr3SYX/DgRS0qIvuOO4coOUxO6ycwRnzFhJcI1juEIMdJv65EGDAxphgbmh3zLuLBF8wd1Onkw==", + "dependencies": { + "@apollo/client": "^3.5.6", + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@graphql-tools/schema": "7.1.5", + "@mui/icons-material": "^5.11.9", + "@mui/material": "^5.11.10", + "@mui/system": "^5.14.18", + "@mui/x-tree-view": "^6.17.0", + "batching-toposort": "^1.2.0", + "classnames": "^2.3.1", + "d3": "^7.6.1", + "d3-fetch": "^2.0.0", + "d3-interpolate": "^2.0.1", + "d3-interpolate-path": "^2.2.3", + "d3-scale": "^3.3.0", + "d3-selection": "^2.0.0", + "d3-shape": "^2.1.0", + "d3-transition": "^2.0.0", + "d3-zoom": "^2.0.0", + "dayjs": "^1.10.7", + "deepmerge": "^4.2.2", + "fetch-mock": "^9.11.0", + "fishery": "^1.4.0", + "graphql": "^15.8.0", + "graphql-type-json": "^0.3.2", + "highlight.js": "^10.7.3", + "kiwi.js": "^1.1.3", + "lodash": "^4.17.21", + "plotly.js-dist-min": "^2.26.0", + "react-content-loader": "^6.2.0", + "react-csv": "^2.2.2", + "react-custom-scrollbars-2": "^4.5.0", + "react-json-view": "^1.21.3", + "react-plotly.js": "^2.5.1", + "react-redux": "^8.1.3", + "react-router": "^5.2.1", + "react-router-dom": "^5.3.0", + "redux": "^4.1.2", + "redux-mock-store": "^1.5.4", + "redux-thunk": "^2.4.1", + "redux-watch": "^1.2.0", + "reselect": "^4.1.5", + "seedrandom": "^3.0.5", + "sinon": "^12.0.1", + "svg-crowbar": "^0.6.5", + "uuid": "^9.0.0", + "what-input": "^5.2.10" + }, + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -21424,16 +21424,16 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/ua-parser-js": {