Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support settings of custom project path in Kedro Extension #162

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions bundled/tool/lsp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def _set_project_with_workspace(self):
try:
self.workspace_settings = next(iter(WORKSPACE_SETTINGS.values()))
root_path = pathlib.Path(
self.workspace.root_path
self.workspace_settings.get("kedroProjectPath") or self.workspace.root_path
) # todo: From language server, can we get it from client initialise response instead?
project_metadata = bootstrap_project(root_path)
env = None
Expand Down Expand Up @@ -489,6 +489,7 @@ def _get_global_defaults():
"importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"),
"showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"),
"environment": GLOBAL_SETTINGS.get("environment", ""),
"kedroProjectPath": GLOBAL_SETTINGS.get("kedroProjectPath", ""),
}


Expand Down Expand Up @@ -578,7 +579,9 @@ def get_project_data_from_viz(lsClient):

data = None
try:
load_and_populate_data(Path.cwd())
workspace_settings = next(iter(WORKSPACE_SETTINGS.values()))
kedro_project_path = Path(workspace_settings.get("kedroProjectPath")) or Path.cwd()
load_and_populate_data(kedro_project_path)
data = get_kedro_project_json_data()
return data
except Exception as e:
Expand Down
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@
],
"scope": "machine",
"type": "string"
},
"kedro.kedroProjectPath": {
"default": "",
"description": "Custom path to Kedro project root directory. Please add absolute path to your Kedro project root directory.",
"scope": "resource",
"type": "string"
}
}
},
Expand Down Expand Up @@ -167,6 +173,11 @@
"command": "kedro.showOutputChannel",
"category": "kedro",
"title": "Show logs"
},
{
"command": "kedro.kedroProjectPath",
"title": "Kedro: Set Project Path",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

The Kedro: is not needed

Suggested change
"title": "Kedro: Set Project Path",
"title": "Set Project Path",

"category": "kedro"
}
]
},
Expand Down
76 changes: 69 additions & 7 deletions src/common/commands.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,90 @@
import * as fs from 'fs';
import { QuickPickItem, window } from 'vscode';
import * as path from 'path';
import { QuickPickItem, window, Uri } from 'vscode';
import * as vscode from 'vscode';

import { getWorkspaceFolders } from './vscodeapi';
import { LanguageClient, LanguageClientOptions, ServerOptions, State, integer } from 'vscode-languageclient/node';
import { LanguageClient, State } from 'vscode-languageclient/node';
import { isKedroProject } from './utilities';
export async function selectEnvironment() {
let workspaces = getWorkspaceFolders();
const root_dir = workspaces[0].uri.fsPath; // Only pick the first workspace
const confDir = `${root_dir}/conf`;
const config = vscode.workspace.getConfiguration('kedro');
let kedroProjectPath = config.get<string>('kedroProjectPath');
let kedroProjectRootDir: string | undefined = undefined;

if (kedroProjectPath) {
kedroProjectRootDir = kedroProjectPath;
} else {
let workspaces = getWorkspaceFolders();
kedroProjectRootDir = workspaces[0].uri.fsPath; // Only pick the first workspace
}

const confDir = `${kedroProjectRootDir}/conf`;
// Iterate the `conf` directory to get folder names
const directories = fs
.readdirSync(confDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);

const envs: QuickPickItem[] = directories.filter(dir => dir !== 'base').map((label) => ({ label }));
const envs: QuickPickItem[] = directories.filter((dir) => dir !== 'base').map((label) => ({ label }));

const result = await window.showQuickPick(envs, {
placeHolder: 'Select Kedro runtime environment',
});

return result;
}

export async function setKedroProjectPath() {
const result = await vscode.window.showInputBox({
placeHolder: 'Enter the Kedro Project Root Directory',
prompt: 'Please provide the path to the Kedro project root directory',
validateInput: async (value) => {
if (!value) {
return 'Path cannot be empty';
}
// Verify if path exists and is a Kedro project
if (!(await isKedroProject(value))) {
return 'Invalid Kedro project path. Please ensure it contains pyproject.toml';
}
return null;
},
});

if (result) {
// Create URI from the path
const uri = vscode.Uri.file(result);

// Get current workspace folders
const currentFolders = vscode.workspace.workspaceFolders || [];

// Check if the entered path is already part of any workspace folder
const isPartOfWorkspace = currentFolders.some((folder) => {
const folderPath = folder.uri.fsPath;
return result.startsWith(folderPath) || folderPath.startsWith(result);
});

// If path is not part of workspace, add it as a new workspace folder
if (!isPartOfWorkspace) {
// Add new folder to workspace
const success = await vscode.workspace.updateWorkspaceFolders(
currentFolders.length,
0,
{ uri: uri, name: path.basename(result) }, // New folder to add
);

if (!success) {
vscode.window.showErrorMessage('Failed to add folder to workspace');
return;
}
}

// Update kedro configuration
const config = vscode.workspace.getConfiguration('kedro');
await config.update('kedroProjectPath', result, vscode.ConfigurationTarget.Workspace);
vscode.window.showInformationMessage('Kedro project path updated successfully');
}
}

let logger: vscode.LogOutputChannel;

/**
Expand Down Expand Up @@ -113,4 +176,3 @@ export async function executeGetProjectDataCommand(lsClient: LanguageClient | un
const result = await vscode.commands.executeCommand(commandName);
return result;
}

4 changes: 3 additions & 1 deletion src/common/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ async function createServer(
environment?: string,
): Promise<LanguageClient> {
const command = settings.interpreter[0];
const cwd = settings.cwd;

// Use kedroProjectPath if set, otherwise fallback to settings.cwd
const cwd = settings.kedroProjectPath || settings.cwd;

// Set debugger path needed for debugging python code.
const newEnv = { ...process.env };
Expand Down
4 changes: 4 additions & 0 deletions src/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface ISettings {
showNotifications: string;
isExperimental: string;
environment: string;
kedroProjectPath: string;
}

export function getExtensionSettings(namespace: string, includeInterpreter?: boolean): Promise<ISettings[]> {
Expand Down Expand Up @@ -73,6 +74,7 @@ export async function getWorkspaceSettings(
showNotifications: config.get<string>(`showNotifications`) ?? 'off',
isExperimental: config.get<string>(`isExperimental`) ?? 'yes',
environment: config.get<string>(`environment`) ?? '',
kedroProjectPath: config.get<string>(`kedroProjectPath`) ?? '',
};
return workspaceSetting;
}
Expand Down Expand Up @@ -103,6 +105,7 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?:
showNotifications: getGlobalValue<string>(config, 'showNotifications', 'off'),
isExperimental: getGlobalValue<string>(config, 'isExperimental', 'yes'),
environment: getGlobalValue<string>(config, 'environment', ''),
kedroProjectPath: getGlobalValue<string>(config, 'kedroProjectPath', ''),
};
return setting;
}
Expand All @@ -115,6 +118,7 @@ export function checkIfConfigurationChanged(e: ConfigurationChangeEvent, namespa
`${namespace}.importStrategy`,
`${namespace}.showNotifications`,
`${namespace}.environment`,
`${namespace}.kedroProjectPath`,
];
const changed = settings.map((s) => e.affectsConfiguration(s));
return changed.includes(true);
Expand Down
53 changes: 39 additions & 14 deletions src/common/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ export function getLSClientTraceLevel(channelLogLevel: LogLevel, globalLogLevel:
}

export async function getProjectRoot(): Promise<WorkspaceFolder> {
const config = vscode.workspace.getConfiguration('kedro');
let kedroProjectPath = config.get<string>('kedroProjectPath');

if (kedroProjectPath && kedroProjectPath.trim()) {
return {
uri: Uri.file(kedroProjectPath),
name: path.basename(kedroProjectPath),
index: 0,
};
}

const workspaces: readonly WorkspaceFolder[] = getWorkspaceFolders();
if (workspaces.length === 0) {
return {
Expand Down Expand Up @@ -132,8 +143,11 @@ export async function checkKedroViz(context: vscode.ExtensionContext): Promise<b

export async function checkKedroProjectConsent(context: vscode.ExtensionContext): Promise<Boolean> {
const pathToScript = 'bundled/tool/check_consent.py';
const config = vscode.workspace.getConfiguration('kedro');
let rootDir = config.get<string>('kedroProjectPath') || EXTENSION_ROOT_DIR;

try {
const stdout = await callPythonScript(pathToScript, EXTENSION_ROOT_DIR, context);
const stdout = await callPythonScript(pathToScript, rootDir, context);
const telemetryResult = parseTelemetryConsent(stdout);

// Check if the script output contains the success message
Expand Down Expand Up @@ -177,26 +191,37 @@ export async function updateKedroVizPanel(lsClient: LanguageClient | undefined):
KedroVizPanel.currentPanel?.updateData(projectData);
}

export async function isKedroProject(): Promise<boolean> {
export async function isKedroProject(kedroProjectPath?: string): Promise<boolean> {
if (kedroProjectPath && kedroProjectPath.trim()) {
return await checkPyprojectToml(kedroProjectPath);
}

// No specific path: check all workspace folders
const folders = getWorkspaceFolders();
if (!folders || folders.length === 0) {
return false;
}

// Check all workspace folders
for (const folder of folders) {
const pyprojectPath = path.join(folder.uri.fsPath, 'pyproject.toml');
try {
const content = await fs.readFile(pyprojectPath, 'utf8');
if (content.includes('[tool.kedro]')) {
traceLog(`Kedro project detected in folder: ${folder.uri.fsPath}`);
return true;
}
} catch (error) {
// Continue if we can't find/read files
traceError(`Error reading ${pyprojectPath}: ${error}`);
continue;
if (await checkPyprojectToml(folder.uri.fsPath)) {
return true;
}
}

return false;
}

async function checkPyprojectToml(projectPath: string): Promise<boolean> {
const pyprojectPath = path.join(projectPath, 'pyproject.toml');
try {
const content = await fs.readFile(pyprojectPath, 'utf8');
if (content.includes('[tool.kedro]')) {
traceLog(`Kedro project detected at ${projectPath}`);
return true;
}
} catch (error) {
// Only log the error if needed, otherwise we silently fail
traceError(`Error reading ${pyprojectPath}: ${error}`);
}
return false;
}
Loading
Loading