Skip to content

Commit 30a6672

Browse files
committed
handling errors and retrieving kernel names
1 parent 6991d99 commit 30a6672

File tree

6 files changed

+151
-41
lines changed

6 files changed

+151
-41
lines changed

pythonFiles/PythonTools/ipythonServer.py

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,12 @@
6060
if jupyter_era:
6161
# Jupyter / IPython 4.x
6262
from jupyter_client import KernelManager
63+
from jupyter_client.kernelspec import KernelSpecManager
64+
kernelSpecManager = KernelSpecManager()
6365
else:
6466
from IPython.kernel import KernelManager
67+
from IPython.kernel.kernelspec import KernelSpecManager
68+
kernelSpecManager = KernelSpecManager()
6569

6670
# End of the great "support IPython 2, 3, 4" strat
6771

@@ -76,6 +80,11 @@ def _debug_write(out):
7680
sys.__stdout__.flush()
7781

7882

83+
def listKernelNames():
84+
"""Returns a dict mapping kernel names to resource directories."""
85+
return list(kernelSpecManager.find_kernel_specs().keys())
86+
87+
7988
class IPythonExitException(Exception):
8089
pass
8190

@@ -105,7 +114,6 @@ def acquire(self):
105114
def release(self):
106115
self.lock.release()
107116

108-
109117
class iPythonSocketServer(object):
110118
"""back end for executing REPL code. This base class handles all of the
111119
communication with the remote process while derived classes implement the
@@ -114,6 +122,8 @@ class iPythonSocketServer(object):
114122
"""Messages sent back as responses"""
115123
_PONG = to_bytes('PONG')
116124
_EXIT = to_bytes('EXIT')
125+
_LSTK = to_bytes('LSTK')
126+
_EROR = to_bytes('EROR')
117127

118128
def __init__(self):
119129
import threading
@@ -164,18 +174,24 @@ def start_processing(self):
164174
try:
165175
inp = read_bytes(self.conn, 4)
166176

167-
#self.conn.settimeout(None)
177+
# self.conn.settimeout(None)
168178
_debug_write('Command bytes received: ')
169-
_debug_write('Command bytes received: ' + str(inp))
170179

171180
cmd = iPythonSocketServer._COMMANDS.get(inp)
172-
if inp:
173-
if cmd is not None:
174-
cmd(self)
175-
else:
176-
if inp:
177-
print ('unknown command', inp)
178-
break
181+
if inp and cmd is not None:
182+
id = ""
183+
try:
184+
if iPythonSocketServer._COMMANDS_WITH_IDS.get(inp) == True:
185+
id = read_string(self.conn)
186+
cmd(self, id)
187+
else:
188+
cmd(self)
189+
except:
190+
self.replyWithError(inp, id)
191+
else:
192+
if inp:
193+
print ('unknown command', inp)
194+
break
179195
except socket.timeout:
180196
pass
181197

@@ -200,15 +216,13 @@ def start_processing(self):
200216
def check_for_exit_socket_loop(self):
201217
return self.exit_requested
202218

203-
def _cmd_run(self):
204-
"""runs the received snippet of code"""
205-
# self.run_command(read_string(self.conn))
206-
pass
207-
208-
def _cmd_abrt(self):
209-
"""aborts the current running command"""
210-
# abort command, interrupts execution of the main thread.
211-
pass
219+
def replyWithError(self, cmd, id):
220+
with self.send_lock:
221+
_debug_write('Replying with error')
222+
write_bytes(self.conn, iPythonSocketServer._EROR)
223+
write_string(self.conn, cmd)
224+
write_string(self.conn, "" if id is None else id)
225+
write_string(self.conn, str(traceback.format_exc()))
212226

213227
def _cmd_exit(self):
214228
"""exits the interactive process"""
@@ -224,6 +238,26 @@ def _cmd_ping(self):
224238
write_bytes(self.conn, iPythonSocketServer._PONG)
225239
write_string(self.conn, "pong received with message" + message)
226240

241+
def _cmd_lstk(self, id):
242+
"""List kernel specs"""
243+
_debug_write('Listing kernel specs')
244+
kernelspecs = json.dumps(listKernelNames())
245+
with self.send_lock:
246+
_debug_write('Replying with kernels = ' + kernelspecs)
247+
write_bytes(self.conn, iPythonSocketServer._LSTK)
248+
write_string(self.conn, id)
249+
write_string(self.conn, kernelspecs)
250+
251+
def _cmd_run(self):
252+
"""runs the received snippet of code"""
253+
# self.run_command(read_string(self.conn))
254+
pass
255+
256+
def _cmd_abrt(self):
257+
"""aborts the current running command"""
258+
# abort command, interrupts execution of the main thread.
259+
pass
260+
227261
def _cmd_inpl(self):
228262
"""handles the input command which returns a string of input"""
229263
self.input_string = read_string(self.conn)
@@ -304,6 +338,11 @@ def flush(self):
304338
to_bytes('exit'): _cmd_exit,
305339
to_bytes('ping'): _cmd_ping,
306340
to_bytes('inpl'): _cmd_inpl,
341+
to_bytes('lstk'): _cmd_lstk,
342+
}
343+
344+
_COMMANDS_WITH_IDS = {
345+
to_bytes('lstk'): True,
307346
}
308347

309348

src/client/common/comms/SocketStream.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class SocketStream {
4343
this.ClearErrors();
4444
}
4545
public EndTransaction() {
46-
this.isInTransaction = true;
46+
this.isInTransaction = false;
4747
this.buffer = this.buffer.slice(this.bytesRead);
4848
this.bytesRead = 0;
4949
this.ClearErrors();
@@ -212,12 +212,12 @@ export class SocketStream {
212212
}
213213
}
214214
if (this.HasInsufficientDataForReading) {
215-
if (!startedTransaction) {
215+
if (startedTransaction) {
216216
this.RollBackTransaction();
217217
}
218218
return undefined;
219219
}
220-
if (!startedTransaction) {
220+
if (startedTransaction) {
221221
this.EndTransaction();
222222
}
223223
return data;

src/client/jupyter/comms/commands.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
export class Commands {
44
public static ExitCommandBytes: Buffer = new Buffer("exit");
55
public static PingBytes: Buffer = new Buffer("ping");
6+
public static ListKernelsBytes: Buffer = new Buffer("lstk");
67
}
78

89
export namespace ResponseCommands {
9-
export const PONG = 'PONG';
10+
export const Pong = 'PONG';
11+
export const ListKernels = 'LSTK';
12+
export const Error = 'EROR';
1013
}

src/client/jupyter/comms/ipythonAdapter.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ import { SocketCallbackHandler } from "../../common/comms/socketCallbackHandler"
55
import { Commands, ResponseCommands } from "./commands";
66
import { SocketStream } from "../../Common/comms/SocketStream";
77
import { SocketServer } from '../../common/comms/socketServer';
8+
import { IdDispenser } from '../../debugger/Common/Utils';
9+
import { createDeferred, Deferred } from '../../common/helpers';
810

911
export class iPythonAdapter extends SocketCallbackHandler {
12+
private idDispenser: IdDispenser;
1013
constructor(socketServer: SocketServer) {
1114
super(socketServer);
12-
this.registerCommandHandler(ResponseCommands.PONG, this.onPong.bind(this));
15+
this.registerCommandHandler(ResponseCommands.Pong, this.onPong.bind(this));
16+
this.registerCommandHandler(ResponseCommands.ListKernels, this.onKernelsListed.bind(this));
17+
this.registerCommandHandler(ResponseCommands.ListKernels, this.onKernelsListed.bind(this));
18+
this.registerCommandHandler(ResponseCommands.Error, this.onError.bind(this));
19+
this.idDispenser = new IdDispenser();
1320
}
1421

1522
private pid: number;
@@ -33,6 +40,40 @@ export class iPythonAdapter extends SocketCallbackHandler {
3340
return true;
3441
}
3542

43+
private pendingCommands = new Map<string, Deferred<any>>();
44+
45+
public listKernels(): Promise<string[]> {
46+
const def = createDeferred<string[]>()
47+
const id = this.idDispenser.Allocate().toString();
48+
this.pendingCommands.set(id, def);
49+
this.SendRawCommand(Commands.ListKernelsBytes);
50+
this.stream.WriteString(id);
51+
52+
return def.promise;
53+
}
54+
55+
private onKernelsListed() {
56+
const id = this.stream.readStringInTransaction();
57+
const kernels = this.stream.readStringInTransaction();
58+
if (kernels == undefined) {
59+
return;
60+
}
61+
62+
const def = this.pendingCommands.get(id);
63+
this.pendingCommands.delete(id);
64+
65+
let kernelList: string[];
66+
try {
67+
kernelList = JSON.parse(kernels)
68+
}
69+
catch (ex) {
70+
def.reject(ex);
71+
return;
72+
}
73+
74+
def.resolve(kernelList);
75+
}
76+
3677
public ping() {
3778
this.SendRawCommand(Commands.PingBytes);
3879
this.stream.WriteString('Hello world from Type Script - Функция проверки ИНН и КПП - 长城!')
@@ -45,4 +86,19 @@ export class iPythonAdapter extends SocketCallbackHandler {
4586
}
4687
this.emit("pong", message);
4788
}
89+
90+
private onError() {
91+
const cmd = this.stream.readStringInTransaction();
92+
const id = this.stream.readStringInTransaction();
93+
const trace = this.stream.readStringInTransaction();
94+
if (trace == undefined) {
95+
return;
96+
}
97+
if (id.length > 0 && this.pendingCommands.has(id)) {
98+
const def = this.pendingCommands.get(id);
99+
this.pendingCommands.delete(id);
100+
def.reject(trace);
101+
}
102+
this.emit("onerror", { command: cmd, id: id, trace: trace });
103+
}
48104
}

src/client/jupyter/comms/main.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SocketServer } from '../../common/comms/socketServer';
33
import * as child_process from 'child_process';
44
import * as path from 'path';
55
import * as vscode from 'vscode';
6+
import {createDeferred} from '../../common/helpers';
67

78
export class Main {
89
constructor(private outputChannel: vscode.OutputChannel) {
@@ -15,11 +16,13 @@ export class Main {
1516
public start() {
1617
const pyFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'ipythonServer.py');
1718
this.startSocketServer().then(port => {
19+
const def = createDeferred<any>();
1820
const newEnv = { "DEBUG_DJAYAMANNE_IPYTHON": "1" };
1921
Object.assign(newEnv, process.env);
20-
this.process = child_process.spawn('python3', [pyFile, port.toString()], { env: newEnv });
22+
this.process = child_process.spawn('python', [pyFile, port.toString()], { env: newEnv });
2123
this.process.stdout.setEncoding('utf8');
2224
this.process.stderr.setEncoding('utf8');
25+
2326
this.process.stdout.on('data', (data: string) => {
2427
this.outputChannel.append(data);
2528
});
@@ -30,7 +33,12 @@ export class Main {
3033
setTimeout(() => {
3134
//this.socketServer.
3235
this.ipythonAdapter.ping();
33-
}, 10000);
36+
this.ipythonAdapter.listKernels().then(x => {
37+
const y = x;
38+
}, reason => {
39+
const y = reason;
40+
})
41+
}, 1000);
3442
});
3543
}
3644
private startSocketServer(): Promise<number> {

src/client/jupyter/main.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { JupyterSymbolProvider } from './editorIntegration/symbolProvider';
99
import { formatErrorForLogging } from '../common/utils';
1010
import * as telemetryHelper from '../common/telemetry';
1111
import * as telemetryContracts from '../common/telemetryContracts';
12+
import * as main from './comms/main';
1213

1314
// Todo: Refactor the error handling and displaying of messages
1415

@@ -78,22 +79,25 @@ export class Jupyter extends vscode.Disposable {
7879
this.status.setActiveKernel(this.kernel ? this.kernel.kernelSpec : null);
7980
}
8081
executeCode(code: string, language: string): Promise<any> {
81-
telemetryHelper.sendTelemetryEvent(telemetryContracts.Jupyter.Usage);
82+
const m = new main.Main(this.outputChannel);
83+
m.start();
84+
return Promise.resolve();
85+
// telemetryHelper.sendTelemetryEvent(telemetryContracts.Jupyter.Usage);
8286

83-
if (this.kernel && this.kernel.kernelSpec.language === language) {
84-
return this.executeAndDisplay(this.kernel, code);
85-
}
86-
return this.kernelManager.startKernelFor(language)
87-
.then(kernel => {
88-
if (kernel) {
89-
this.onKernelChanged(kernel);
90-
return this.executeAndDisplay(kernel, code);
91-
}
92-
}).catch(reason => {
93-
const message = typeof reason === 'string' ? reason : reason.message;
94-
vscode.window.showErrorMessage(message);
95-
this.outputChannel.appendLine(formatErrorForLogging(reason));
96-
});
87+
// if (this.kernel && this.kernel.kernelSpec.language === language) {
88+
// return this.executeAndDisplay(this.kernel, code);
89+
// }
90+
// return this.kernelManager.startKernelFor(language)
91+
// .then(kernel => {
92+
// if (kernel) {
93+
// this.onKernelChanged(kernel);
94+
// return this.executeAndDisplay(kernel, code);
95+
// }
96+
// }).catch(reason => {
97+
// const message = typeof reason === 'string' ? reason : reason.message;
98+
// vscode.window.showErrorMessage(message);
99+
// this.outputChannel.appendLine(formatErrorForLogging(reason));
100+
// });
97101
}
98102
private executeAndDisplay(kernel: Kernel, code: string) {
99103
return this.executeCodeInKernel(kernel, code).then(result => {

0 commit comments

Comments
 (0)