From e167676419d2c17ea2664afc391be4dab7d59b69 Mon Sep 17 00:00:00 2001 From: Paula Camargo Date: Tue, 19 Jul 2022 17:36:42 -0700 Subject: [PATCH 1/2] Add dynamic FastAPI debug config --- .../dynamicdebugConfigurationService.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts b/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts index 30783227cd45..7a758c8cfb4e 100644 --- a/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts +++ b/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts @@ -44,6 +44,38 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf justMyCode: true, }); } + + let fastApiPath = await this.getFastApiPath(folder); + + if (fastApiPath) { + fastApiPath = path + .relative(folder.uri.fsPath, fastApiPath) + .replaceAll(this.pathUtils.separator, '.') + .replace('.py', ''); + + providers.push({ + name: 'Dynamic Python: FastAPI', + type: DebuggerTypeName, + request: 'launch', + module: 'uvicorn', + args: [`${fastApiPath}:app`], + jinja: true, + justMyCode: true, + }); + } + return providers; } + + private async getFastApiPath(folder: WorkspaceFolder) { + const mainPaths = await this.fs.search(path.join(folder.uri.fsPath, '**/main.py')); + const appPaths = await this.fs.search(path.join(folder.uri.fsPath, '**/app.py')); + const possiblePaths = [...mainPaths, ...appPaths]; + const regExpression = /app\s*=\s*FastAPI\(/; + const flaskPaths = possiblePaths.filter((applicationPath) => + regExpression.exec(this.fs.readFileSync(applicationPath).toString()), + ); + + return flaskPaths.length ? flaskPaths[0] : null; + } } From 6b1793e40d3c882c99e87a3b8d72958ef13992b9 Mon Sep 17 00:00:00 2001 From: Paula Camargo Date: Thu, 21 Jul 2022 13:53:09 -0700 Subject: [PATCH 2/2] Improve performance --- .../dynamicdebugConfigurationService.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts b/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts index 7a758c8cfb4e..032a38b6740c 100644 --- a/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts +++ b/src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts @@ -10,6 +10,7 @@ import { IDynamicDebugConfigurationService } from '../types'; import { IFileSystem } from '../../../common/platform/types'; import { IPathUtils } from '../../../common/types'; import { DebuggerTypeName } from '../../constants'; +import { asyncFilter } from '../../../common/utils/arrayUtils'; const workspaceFolderToken = '${workspaceFolder}'; @@ -31,14 +32,13 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf justMyCode: true, }); - const djangoManagePath = await this.fs.search(path.join(folder.uri.fsPath, '**/manage.py')); - if (djangoManagePath.length) { - const managePath = path.relative(folder.uri.fsPath, djangoManagePath[0]); + const djangoManagePath = await this.getDjangoPath(folder); + if (djangoManagePath) { providers.push({ name: 'Dynamic Python: Django', type: DebuggerTypeName, request: 'launch', - program: `${workspaceFolderToken}${this.pathUtils.separator}${managePath}`, + program: `${workspaceFolderToken}${this.pathUtils.separator}${djangoManagePath}`, args: ['runserver'], django: true, justMyCode: true, @@ -46,13 +46,11 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf } let fastApiPath = await this.getFastApiPath(folder); - if (fastApiPath) { fastApiPath = path .relative(folder.uri.fsPath, fastApiPath) .replaceAll(this.pathUtils.separator, '.') .replace('.py', ''); - providers.push({ name: 'Dynamic Python: FastAPI', type: DebuggerTypeName, @@ -67,15 +65,31 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf return providers; } + private async getDjangoPath(folder: WorkspaceFolder) { + const possiblePaths = await this.getPossiblePaths(folder, ['manage.py', '*/manage.py']); + + return possiblePaths.length ? path.relative(folder.uri.fsPath, possiblePaths[0]) : null; + } + private async getFastApiPath(folder: WorkspaceFolder) { - const mainPaths = await this.fs.search(path.join(folder.uri.fsPath, '**/main.py')); - const appPaths = await this.fs.search(path.join(folder.uri.fsPath, '**/app.py')); - const possiblePaths = [...mainPaths, ...appPaths]; + const possiblePaths = await this.getPossiblePaths(folder, ['main.py', 'app.py', '*/main.py', '*/app.py']); const regExpression = /app\s*=\s*FastAPI\(/; - const flaskPaths = possiblePaths.filter((applicationPath) => - regExpression.exec(this.fs.readFileSync(applicationPath).toString()), + const flaskPaths = await asyncFilter(possiblePaths, async (possiblePath) => + regExpression.exec((await this.fs.readFile(possiblePath)).toString()), ); return flaskPaths.length ? flaskPaths[0] : null; } + + private async getPossiblePaths(folder: WorkspaceFolder, globPatterns: string[]): Promise { + const foundPathsPromises = (await Promise.allSettled( + globPatterns.map( + async (pattern): Promise => this.fs.search(path.join(folder.uri.fsPath, pattern)), + ), + )) as { status: string; value: [] }[]; + const results: string[] = []; + foundPathsPromises.forEach((result) => results.push(...result.value)); + + return results; + } }