@@ -8,6 +8,7 @@ import * as workspaceApis from '../../../common/workspace.apis';
88import * as execUtils from '../../../features/execution/execUtils' ;
99import { runAsTask } from '../../../features/execution/runAsTask' ;
1010import * as builtinHelpers from '../../../managers/builtin/helpers' ;
11+ import * as pep723Module from '../../../features/execution/pep723' ;
1112
1213suite ( 'runAsTask Tests' , ( ) => {
1314 let mockTraceInfo : sinon . SinonStub ;
@@ -16,6 +17,7 @@ suite('runAsTask Tests', () => {
1617 let mockGetWorkspaceFolder : sinon . SinonStub ;
1718 let mockQuoteStringIfNecessary : sinon . SinonStub ;
1819 let mockShouldUseUv : sinon . SinonStub ;
20+ let mockIsPep723Script : sinon . SinonStub ;
1921
2022 setup ( ( ) => {
2123 mockTraceInfo = sinon . stub ( logging , 'traceInfo' ) ;
@@ -24,6 +26,7 @@ suite('runAsTask Tests', () => {
2426 mockGetWorkspaceFolder = sinon . stub ( workspaceApis , 'getWorkspaceFolder' ) ;
2527 mockQuoteStringIfNecessary = sinon . stub ( execUtils , 'quoteStringIfNecessary' ) ;
2628 mockShouldUseUv = sinon . stub ( builtinHelpers , 'shouldUseUv' ) . resolves ( false ) ;
29+ mockIsPep723Script = sinon . stub ( pep723Module , 'isPep723Script' ) . resolves ( false ) ;
2730 } ) ;
2831
2932 teardown ( ( ) => {
@@ -1172,4 +1175,144 @@ suite('runAsTask Tests', () => {
11721175 assert . ok ( mockTraceWarn . notCalled , 'Should not warn for complete environment configuration' ) ;
11731176 } ) ;
11741177 } ) ;
1178+
1179+ suite ( 'PEP 723 Inline Script Metadata' , ( ) => {
1180+ function makeEnv ( executable : string ) : PythonEnvironment {
1181+ return {
1182+ envId : { id : 'test-env' , managerId : 'test-manager' } ,
1183+ name : 'Test Environment' ,
1184+ displayName : 'Test Environment' ,
1185+ displayPath : '/path/to/env' ,
1186+ version : '3.11.0' ,
1187+ environmentPath : Uri . file ( '/path/to/env' ) ,
1188+ execInfo : {
1189+ run : { executable, args : [ '--env-arg' ] } ,
1190+ } ,
1191+ sysPrefix : '/path/to/env' ,
1192+ } ;
1193+ }
1194+
1195+ test ( 'should run PEP 723 script with uv run <script> and no --python flag' , async ( ) => {
1196+ const environment = makeEnv ( '/path/to/python' ) ;
1197+ const options : PythonTaskExecutionOptions = {
1198+ name : 'PEP 723 Task' ,
1199+ args : [ '/workspace/pep723_script.py' ] ,
1200+ } ;
1201+
1202+ mockGetWorkspaceFolder . returns ( undefined ) ;
1203+ mockShouldUseUv . resolves ( true ) ;
1204+ mockIsPep723Script . withArgs ( '/workspace/pep723_script.py' ) . resolves ( true ) ;
1205+ mockQuoteStringIfNecessary . withArgs ( 'uv' ) . returns ( 'uv' ) ;
1206+ mockExecuteTask . resolves ( { } as TaskExecution ) ;
1207+
1208+ await runAsTask ( environment , options ) ;
1209+
1210+ const taskArg = mockExecuteTask . firstCall . args [ 0 ] as Task ;
1211+ const execution = taskArg . execution as ShellExecution ;
1212+
1213+ assert . strictEqual ( execution . command , 'uv' , 'Should run via uv' ) ;
1214+ assert . deepStrictEqual (
1215+ execution . args ,
1216+ [ 'run' , '/workspace/pep723_script.py' ] ,
1217+ 'PEP 723 script should use uv run <script> with no --python and no env args' ,
1218+ ) ;
1219+ assert . ok (
1220+ mockTraceInfo . calledWith ( sinon . match ( / P E P 7 2 3 s c r i p t d e t e c t e d / ) ) ,
1221+ 'Should log that PEP 723 was detected' ,
1222+ ) ;
1223+ } ) ;
1224+
1225+ test ( 'should pass extra user args after the script for PEP 723' , async ( ) => {
1226+ const environment = makeEnv ( '/path/to/python' ) ;
1227+ const options : PythonTaskExecutionOptions = {
1228+ name : 'PEP 723 Task With Args' ,
1229+ args : [ '/workspace/pep723_script.py' , '--verbose' , '--output' , 'out.txt' ] ,
1230+ } ;
1231+
1232+ mockGetWorkspaceFolder . returns ( undefined ) ;
1233+ mockShouldUseUv . resolves ( true ) ;
1234+ mockIsPep723Script . withArgs ( '/workspace/pep723_script.py' ) . resolves ( true ) ;
1235+ mockQuoteStringIfNecessary . withArgs ( 'uv' ) . returns ( 'uv' ) ;
1236+ mockExecuteTask . resolves ( { } as TaskExecution ) ;
1237+
1238+ await runAsTask ( environment , options ) ;
1239+
1240+ const execution = ( mockExecuteTask . firstCall . args [ 0 ] as Task ) . execution as ShellExecution ;
1241+ assert . deepStrictEqual (
1242+ execution . args ,
1243+ [ 'run' , '/workspace/pep723_script.py' , '--verbose' , '--output' , 'out.txt' ] ,
1244+ 'Extra user args should be appended after the script path for PEP 723' ,
1245+ ) ;
1246+ } ) ;
1247+
1248+ test ( 'should skip PEP 723 detection when first arg starts with a flag' , async ( ) => {
1249+ // When args[0] is a flag (e.g. -m), isPep723Script should not be called
1250+ const environment = makeEnv ( '/path/to/python' ) ;
1251+ const options : PythonTaskExecutionOptions = {
1252+ name : 'Module Task' ,
1253+ args : [ '-m' , 'pytest' ] ,
1254+ } ;
1255+
1256+ mockGetWorkspaceFolder . returns ( undefined ) ;
1257+ mockShouldUseUv . resolves ( true ) ;
1258+ mockQuoteStringIfNecessary . withArgs ( 'uv' ) . returns ( 'uv' ) ;
1259+ mockExecuteTask . resolves ( { } as TaskExecution ) ;
1260+
1261+ await runAsTask ( environment , options ) ;
1262+
1263+ assert . ok ( mockIsPep723Script . notCalled , 'Should not check PEP 723 when first arg is a flag' ) ;
1264+
1265+ const execution = ( mockExecuteTask . firstCall . args [ 0 ] as Task ) . execution as ShellExecution ;
1266+ // Should fall through to standard uv --python path
1267+ assert . strictEqual ( execution . args ?. [ 0 ] , 'run' ) ;
1268+ assert . strictEqual ( execution . args ?. [ 1 ] , '--python' ) ;
1269+ } ) ;
1270+
1271+ test ( 'should use standard uv --python path for non-PEP 723 scripts' , async ( ) => {
1272+ // Standard .py file with no PEP 723 block → normal uv run --python behavior
1273+ const environment = makeEnv ( '/path/to/python' ) ;
1274+ const options : PythonTaskExecutionOptions = {
1275+ name : 'Standard Script Task' ,
1276+ args : [ '/workspace/regular_script.py' ] ,
1277+ } ;
1278+
1279+ mockGetWorkspaceFolder . returns ( undefined ) ;
1280+ mockShouldUseUv . resolves ( true ) ;
1281+ mockIsPep723Script . withArgs ( '/workspace/regular_script.py' ) . resolves ( false ) ;
1282+ mockQuoteStringIfNecessary . withArgs ( 'uv' ) . returns ( 'uv' ) ;
1283+ mockExecuteTask . resolves ( { } as TaskExecution ) ;
1284+
1285+ await runAsTask ( environment , options ) ;
1286+
1287+ const execution = ( mockExecuteTask . firstCall . args [ 0 ] as Task ) . execution as ShellExecution ;
1288+ assert . strictEqual ( execution . command , 'uv' ) ;
1289+ assert . deepStrictEqual (
1290+ execution . args ,
1291+ [ 'run' , '--python' , '/path/to/python' , '--env-arg' , '/workspace/regular_script.py' ] ,
1292+ 'Non-PEP 723 scripts should use uv run --python <interpreter> with env args' ,
1293+ ) ;
1294+ } ) ;
1295+
1296+ test ( 'should treat isPep723Script read error as non-PEP 723 (graceful fallback)' , async ( ) => {
1297+ const environment = makeEnv ( '/path/to/python' ) ;
1298+ const options : PythonTaskExecutionOptions = {
1299+ name : 'Unreadable Script Task' ,
1300+ args : [ '/workspace/missing_script.py' ] ,
1301+ } ;
1302+
1303+ mockGetWorkspaceFolder . returns ( undefined ) ;
1304+ mockShouldUseUv . resolves ( true ) ;
1305+ // isPep723Script returns false on read error (per implementation), but also verify
1306+ // that even if it somehow throws, runAsTask falls back gracefully
1307+ mockIsPep723Script . withArgs ( '/workspace/missing_script.py' ) . resolves ( false ) ;
1308+ mockQuoteStringIfNecessary . withArgs ( 'uv' ) . returns ( 'uv' ) ;
1309+ mockExecuteTask . resolves ( { } as TaskExecution ) ;
1310+
1311+ await runAsTask ( environment , options ) ;
1312+
1313+ const execution = ( mockExecuteTask . firstCall . args [ 0 ] as Task ) . execution as ShellExecution ;
1314+ // Falls back to standard uv --python path
1315+ assert . strictEqual ( execution . args ?. [ 1 ] , '--python' , 'Should fall back to --python when script is unreadable' ) ;
1316+ } ) ;
1317+ } ) ;
11751318} ) ;
0 commit comments