forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpythonServer.ts
More file actions
103 lines (90 loc) · 3.44 KB
/
pythonServer.ts
File metadata and controls
103 lines (90 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import * as path from 'path';
import * as ch from 'child_process';
import * as rpc from 'vscode-jsonrpc/node';
import { Disposable, window } from 'vscode';
import { EXTENSION_ROOT_DIR } from '../constants';
import { traceError, traceLog } from '../logging';
import { captureTelemetry } from '../telemetry';
import { EventName } from '../telemetry/constants';
const SERVER_PATH = path.join(EXTENSION_ROOT_DIR, 'python_files', 'python_server.py');
let serverInstance: PythonServer | undefined;
export interface PythonServer extends Disposable {
execute(code: string): Promise<string>;
interrupt(): void;
input(): void;
checkValidCommand(code: string): Promise<boolean>;
}
class PythonServerImpl implements Disposable {
private readonly disposables: Disposable[] = [];
constructor(private connection: rpc.MessageConnection, private pythonServer: ch.ChildProcess) {
this.initialize();
this.input();
}
private initialize(): void {
this.disposables.push(
this.connection.onNotification('log', (message: string) => {
console.log('Log:', message);
}),
);
this.connection.listen();
}
public input(): void {
// Register input request handler
this.connection.onRequest('input', async (request) => {
// Ask for user input via popup quick input, send it back to Python
let userPrompt = 'Enter your input here: ';
if (request && request.prompt) {
userPrompt = request.prompt;
}
const input = await window.showInputBox({
title: 'Input Request',
prompt: userPrompt,
ignoreFocusOut: true,
});
return { userInput: input };
});
}
@captureTelemetry(EventName.EXECUTION_CODE, { scope: 'selection' }, false)
public execute(code: string): Promise<string> {
return this.connection.sendRequest('execute', code);
}
public interrupt(): void {
// Passing SIGINT to interrupt only would work for Mac and Linux
if (this.pythonServer.kill('SIGINT')) {
traceLog('Python REPL server interrupted');
}
}
public async checkValidCommand(code: string): Promise<boolean> {
const completeCode = await this.connection.sendRequest('check_valid_command', code);
if (completeCode === 'True') {
return new Promise((resolve) => resolve(true));
}
return new Promise((resolve) => resolve(false));
}
public dispose(): void {
this.connection.sendNotification('exit');
this.disposables.forEach((d) => d.dispose());
this.connection.dispose();
}
}
export function createPythonServer(interpreter: string[]): PythonServer {
if (serverInstance) {
return serverInstance;
}
const pythonServer = ch.spawn(interpreter[0], [...interpreter.slice(1), SERVER_PATH]);
pythonServer.stderr.on('data', (data) => {
traceError(data.toString());
});
pythonServer.on('exit', (code) => {
traceError(`Python server exited with code ${code}`);
});
pythonServer.on('error', (err) => {
traceError(err);
});
const connection = rpc.createMessageConnection(
new rpc.StreamMessageReader(pythonServer.stdout),
new rpc.StreamMessageWriter(pythonServer.stdin),
);
serverInstance = new PythonServerImpl(connection, pythonServer);
return serverInstance;
}