Skip to content

Commit 4bb50dc

Browse files
Copilotanthonykim1
andcommitted
Implement conditional PyREPL disabling based on shell integration and Python version
Co-authored-by: anthonykim1 <[email protected]>
1 parent 1ce9f5e commit 4bb50dc

File tree

4 files changed

+88
-6
lines changed

4 files changed

+88
-6
lines changed

package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"python.tensorBoard.logDirectory.description": "Set this setting to your preferred TensorBoard log directory to skip log directory prompt when starting TensorBoard.",
7474
"python.tensorBoard.logDirectory.markdownDeprecationMessage": "Tensorboard support has been moved to the extension [Tensorboard extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.tensorboard). Instead use the setting `tensorBoard.logDirectory`.",
7575
"python.tensorBoard.logDirectory.deprecationMessage": "Tensorboard support has been moved to the extension Tensorboard extension. Instead use the setting `tensorBoard.logDirectory`.",
76-
"python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things.",
76+
"python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things. Note: PyREPL (available in Python 3.13+) is automatically disabled when shell integration is enabled to avoid cursor indentation issues.",
7777
"python.terminal.activateEnvInCurrentTerminal.description": "Activate Python Environment in the current Terminal on load of the Extension.",
7878
"python.terminal.activateEnvironment.description": "Activate Python Environment in all Terminals created.",
7979
"python.terminal.executeInFileDir.description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder.",

src/client/extensionActivation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ async function activateLegacy(ext: ExtensionState, startupStopWatch: StopWatch):
184184
serviceManager.get<ITerminalAutoActivation>(ITerminalAutoActivation).register();
185185

186186
await registerPythonStartup(ext.context);
187-
await registerBasicRepl(ext.context);
187+
await registerBasicRepl(ext.context, serviceContainer);
188188

189189
serviceManager.get<ICodeExecutionManager>(ICodeExecutionManager).registerCommands();
190190

src/client/terminals/pythonStartup.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { ExtensionContext, Uri } from 'vscode';
55
import * as path from 'path';
66
import { copy, createDirectory, getConfiguration, onDidChangeConfiguration } from '../common/vscodeApis/workspaceApis';
77
import { EXTENSION_ROOT_DIR } from '../constants';
8+
import { IInterpreterService } from '../interpreter/contracts';
9+
import { IServiceContainer } from '../ioc/types';
10+
import { getPythonMinorVersion } from '../repl/replUtils';
811

912
async function applyPythonStartupSetting(context: ExtensionContext): Promise<void> {
1013
const config = getConfiguration('python');
@@ -37,7 +40,37 @@ export async function registerPythonStartup(context: ExtensionContext): Promise<
3740
);
3841
}
3942

40-
export async function registerBasicRepl(context: ExtensionContext): Promise<void> {
41-
// TODO: Configurable by setting
42-
context.environmentVariableCollection.replace('PYTHON_BASIC_REPL', '1');
43+
async function applyBasicReplSetting(context: ExtensionContext, serviceContainer?: IServiceContainer): Promise<void> {
44+
const config = getConfiguration('python');
45+
const shellIntegrationEnabled = config.get<boolean>('terminal.shellIntegration.enabled');
46+
47+
if (shellIntegrationEnabled && serviceContainer) {
48+
// Only disable PyREPL (set PYTHON_BASIC_REPL=1) when shell integration is enabled
49+
// and Python version is 3.13 or higher
50+
try {
51+
const interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
52+
const pythonMinorVersion = await getPythonMinorVersion(undefined, interpreterService);
53+
54+
if ((pythonMinorVersion ?? 0) >= 13) {
55+
context.environmentVariableCollection.replace('PYTHON_BASIC_REPL', '1');
56+
return;
57+
}
58+
} catch {
59+
// If we can't get the Python version, don't set PYTHON_BASIC_REPL
60+
}
61+
}
62+
63+
// Remove PYTHON_BASIC_REPL if shell integration is disabled or Python < 3.13
64+
context.environmentVariableCollection.delete('PYTHON_BASIC_REPL');
65+
}
66+
67+
export async function registerBasicRepl(context: ExtensionContext, serviceContainer?: IServiceContainer): Promise<void> {
68+
await applyBasicReplSetting(context, serviceContainer);
69+
context.subscriptions.push(
70+
onDidChangeConfiguration(async (e) => {
71+
if (e.affectsConfiguration('python.terminal.shellIntegration.enabled')) {
72+
await applyBasicReplSetting(context, serviceContainer);
73+
}
74+
}),
75+
);
4376
}

src/test/terminals/shellIntegration/pythonStartup.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,59 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => {
135135
globalEnvironmentVariableCollection.verify((c) => c.delete('PYTHONSTARTUP'), TypeMoq.Times.once());
136136
});
137137

138-
test('PYTHON_BASIC_REPL is set when registerBasicRepl is called', async () => {
138+
test('PYTHON_BASIC_REPL is set when shell integration is enabled and Python >= 3.13', async () => {
139+
pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => true);
140+
141+
// Mock service container with interpreter service
142+
const serviceContainer = TypeMoq.Mock.ofType<any>();
143+
const interpreterService = TypeMoq.Mock.ofType<any>();
144+
const mockInterpreter = { version: { minor: 13 } };
145+
146+
serviceContainer.setup((sc) => sc.get(TypeMoq.It.isAny())).returns(() => interpreterService.object);
147+
interpreterService.setup((is) => is.getActiveInterpreter(TypeMoq.It.isAny())).returns(() => Promise.resolve(mockInterpreter));
148+
149+
await registerBasicRepl(context.object, serviceContainer.object);
150+
151+
globalEnvironmentVariableCollection.verify(
152+
(c) => c.replace('PYTHON_BASIC_REPL', '1', TypeMoq.It.isAny()),
153+
TypeMoq.Times.once(),
154+
);
155+
});
156+
157+
test('PYTHON_BASIC_REPL is not set when shell integration is disabled', async () => {
158+
pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => false);
159+
139160
await registerBasicRepl(context.object);
161+
140162
globalEnvironmentVariableCollection.verify(
141163
(c) => c.replace('PYTHON_BASIC_REPL', '1', TypeMoq.It.isAny()),
164+
TypeMoq.Times.never(),
165+
);
166+
globalEnvironmentVariableCollection.verify(
167+
(c) => c.delete('PYTHON_BASIC_REPL'),
168+
TypeMoq.Times.once(),
169+
);
170+
});
171+
172+
test('PYTHON_BASIC_REPL is not set when Python < 3.13 even with shell integration enabled', async () => {
173+
pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => true);
174+
175+
// Mock service container with interpreter service for Python 3.12
176+
const serviceContainer = TypeMoq.Mock.ofType<any>();
177+
const interpreterService = TypeMoq.Mock.ofType<any>();
178+
const mockInterpreter = { version: { minor: 12 } };
179+
180+
serviceContainer.setup((sc) => sc.get(TypeMoq.It.isAny())).returns(() => interpreterService.object);
181+
interpreterService.setup((is) => is.getActiveInterpreter(TypeMoq.It.isAny())).returns(() => Promise.resolve(mockInterpreter));
182+
183+
await registerBasicRepl(context.object, serviceContainer.object);
184+
185+
globalEnvironmentVariableCollection.verify(
186+
(c) => c.replace('PYTHON_BASIC_REPL', '1', TypeMoq.It.isAny()),
187+
TypeMoq.Times.never(),
188+
);
189+
globalEnvironmentVariableCollection.verify(
190+
(c) => c.delete('PYTHON_BASIC_REPL'),
142191
TypeMoq.Times.once(),
143192
);
144193
});

0 commit comments

Comments
 (0)