Skip to content

Commit a60f228

Browse files
authored
Correctly display native REPL execution status (microsoft#23797)
Resolves: microsoft#23739
1 parent 0d1a0f1 commit a60f228

File tree

3 files changed

+47
-32
lines changed

3 files changed

+47
-32
lines changed

python_files/python_server.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,38 @@
1313
USER_GLOBALS = {}
1414

1515

16-
def send_message(msg: str):
16+
def _send_message(msg: str):
1717
length_msg = len(msg)
1818
STDOUT.buffer.write(f"Content-Length: {length_msg}\r\n\r\n{msg}".encode())
1919
STDOUT.buffer.flush()
2020

2121

22+
def send_message(**kwargs):
23+
_send_message(json.dumps({"jsonrpc": "2.0", **kwargs}))
24+
25+
2226
def print_log(msg: str):
23-
send_message(json.dumps({"jsonrpc": "2.0", "method": "log", "params": msg}))
27+
send_message(method="log", params=msg)
2428

2529

26-
def send_response(response: str, response_id: int):
27-
send_message(json.dumps({"jsonrpc": "2.0", "id": response_id, "result": response}))
30+
def send_response(
31+
response: str,
32+
response_id: int,
33+
execution_status: bool = True, # noqa: FBT001, FBT002
34+
):
35+
send_message(
36+
id=response_id,
37+
result={"status": execution_status, "output": response},
38+
)
2839

2940

3041
def send_request(params: Optional[Union[List, Dict]] = None):
3142
request_id = uuid.uuid4().hex
3243
if params is None:
33-
send_message(json.dumps({"jsonrpc": "2.0", "id": request_id, "method": "input"}))
44+
send_message(id=request_id, method="input")
3445
else:
35-
send_message(
36-
json.dumps({"jsonrpc": "2.0", "id": request_id, "method": "input", "params": params})
37-
)
46+
send_message(id=request_id, method="input", params=params)
47+
3848
return request_id
3949

4050

@@ -105,24 +115,28 @@ def execute(request, user_globals):
105115
original_stdin = sys.stdin
106116
try:
107117
sys.stdin = str_input
108-
exec_user_input(request["params"], user_globals)
118+
execution_status = exec_user_input(request["params"], user_globals)
109119
finally:
110120
sys.stdin = original_stdin
111-
send_response(str_output.get_value(), request["id"])
121+
122+
send_response(str_output.get_value(), request["id"], execution_status)
112123

113124

114-
def exec_user_input(user_input, user_globals):
125+
def exec_user_input(user_input, user_globals) -> bool:
115126
user_input = user_input[0] if isinstance(user_input, list) else user_input
116127

117128
try:
118129
callable_ = exec_function(user_input)
119130
retval = callable_(user_input, user_globals)
120131
if retval is not None:
121132
print(retval)
133+
return True
122134
except KeyboardInterrupt:
123135
print(traceback.format_exc())
136+
return False
124137
except Exception:
125138
print(traceback.format_exc())
139+
return False
126140

127141

128142
class CustomIO(io.TextIOWrapper):

src/client/repl/pythonServer.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import { EventName } from '../telemetry/constants';
99

1010
const SERVER_PATH = path.join(EXTENSION_ROOT_DIR, 'python_files', 'python_server.py');
1111
let serverInstance: PythonServer | undefined;
12+
export interface ExecutionResult {
13+
status: boolean;
14+
output: string;
15+
}
1216

1317
export interface PythonServer extends Disposable {
14-
execute(code: string): Promise<string>;
18+
execute(code: string): Promise<ExecutionResult | undefined>;
1519
interrupt(): void;
1620
input(): void;
1721
checkValidCommand(code: string): Promise<boolean>;
@@ -52,8 +56,15 @@ class PythonServerImpl implements Disposable {
5256
}
5357

5458
@captureTelemetry(EventName.EXECUTION_CODE, { scope: 'selection' }, false)
55-
public execute(code: string): Promise<string> {
56-
return this.connection.sendRequest('execute', code);
59+
public async execute(code: string): Promise<ExecutionResult | undefined> {
60+
try {
61+
const result = await this.connection.sendRequest('execute', code);
62+
return result as ExecutionResult;
63+
} catch (err) {
64+
const error = err as Error;
65+
traceError(`Error getting response from REPL server:`, error);
66+
}
67+
return undefined;
5768
}
5869

5970
public interrupt(): void {

src/client/repl/replController.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,17 @@ export function createReplController(
2222
for (const cell of cells) {
2323
const exec = controller.createNotebookCellExecution(cell);
2424
exec.start(Date.now());
25-
try {
26-
const result = await server.execute(cell.document.getText());
27-
if (result !== '') {
28-
exec.replaceOutput([
29-
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.text(result, 'text/plain')]),
30-
]);
31-
}
32-
exec.end(true);
33-
} catch (err) {
34-
const error = err as Error;
25+
26+
const result = await server.execute(cell.document.getText());
27+
28+
if (result?.output) {
3529
exec.replaceOutput([
36-
new vscode.NotebookCellOutput([
37-
vscode.NotebookCellOutputItem.error({
38-
name: error.name,
39-
message: error.message,
40-
stack: error.stack,
41-
}),
42-
]),
30+
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.text(result.output, 'text/plain')]),
4331
]);
44-
exec.end(false);
32+
// TODO: Properly update via NotebookCellOutputItem.error later.
4533
}
34+
35+
exec.end(result?.status);
4636
}
4737
};
4838
disposables.push(controller);

0 commit comments

Comments
 (0)