Skip to content

Commit 7c3498c

Browse files
Add dynamic FastAPI debug config (#19505)
* Add dynamic FastAPI debug config * Improve performance
1 parent b090272 commit 7c3498c

File tree

1 file changed

+50
-4
lines changed

1 file changed

+50
-4
lines changed

src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { IDynamicDebugConfigurationService } from '../types';
1010
import { IFileSystem } from '../../../common/platform/types';
1111
import { IPathUtils } from '../../../common/types';
1212
import { DebuggerTypeName } from '../../constants';
13+
import { asyncFilter } from '../../../common/utils/arrayUtils';
1314

1415
const workspaceFolderToken = '${workspaceFolder}';
1516

@@ -31,19 +32,64 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
3132
justMyCode: true,
3233
});
3334

34-
const djangoManagePath = await this.fs.search(path.join(folder.uri.fsPath, '**/manage.py'));
35-
if (djangoManagePath.length) {
36-
const managePath = path.relative(folder.uri.fsPath, djangoManagePath[0]);
35+
const djangoManagePath = await this.getDjangoPath(folder);
36+
if (djangoManagePath) {
3737
providers.push({
3838
name: 'Dynamic Python: Django',
3939
type: DebuggerTypeName,
4040
request: 'launch',
41-
program: `${workspaceFolderToken}${this.pathUtils.separator}${managePath}`,
41+
program: `${workspaceFolderToken}${this.pathUtils.separator}${djangoManagePath}`,
4242
args: ['runserver'],
4343
django: true,
4444
justMyCode: true,
4545
});
4646
}
47+
48+
let fastApiPath = await this.getFastApiPath(folder);
49+
if (fastApiPath) {
50+
fastApiPath = path
51+
.relative(folder.uri.fsPath, fastApiPath)
52+
.replaceAll(this.pathUtils.separator, '.')
53+
.replace('.py', '');
54+
providers.push({
55+
name: 'Dynamic Python: FastAPI',
56+
type: DebuggerTypeName,
57+
request: 'launch',
58+
module: 'uvicorn',
59+
args: [`${fastApiPath}:app`],
60+
jinja: true,
61+
justMyCode: true,
62+
});
63+
}
64+
4765
return providers;
4866
}
67+
68+
private async getDjangoPath(folder: WorkspaceFolder) {
69+
const possiblePaths = await this.getPossiblePaths(folder, ['manage.py', '*/manage.py']);
70+
71+
return possiblePaths.length ? path.relative(folder.uri.fsPath, possiblePaths[0]) : null;
72+
}
73+
74+
private async getFastApiPath(folder: WorkspaceFolder) {
75+
const possiblePaths = await this.getPossiblePaths(folder, ['main.py', 'app.py', '*/main.py', '*/app.py']);
76+
const regExpression = /app\s*=\s*FastAPI\(/;
77+
const flaskPaths = await asyncFilter(possiblePaths, async (possiblePath) =>
78+
regExpression.exec((await this.fs.readFile(possiblePath)).toString()),
79+
);
80+
81+
return flaskPaths.length ? flaskPaths[0] : null;
82+
}
83+
84+
private async getPossiblePaths(folder: WorkspaceFolder, globPatterns: string[]): Promise<string[]> {
85+
const foundPathsPromises = (await Promise.allSettled(
86+
globPatterns.map(
87+
async (pattern): Promise<string[]> => this.fs.search(path.join(folder.uri.fsPath, pattern)),
88+
),
89+
)) as { status: string; value: [] }[];
90+
const results: string[] = [];
91+
foundPathsPromises.forEach((result) => results.push(...result.value));
92+
93+
return results;
94+
}
4995
}

0 commit comments

Comments
 (0)