Skip to content

Commit fe8c4af

Browse files
committed
Added cancellation support for model runs
1 parent 2dce12e commit fe8c4af

File tree

4 files changed

+57
-36
lines changed

4 files changed

+57
-36
lines changed

src/dbt_client/commandProcessExecution.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import { ChildProcess, spawn } from "child_process";
2-
import { EventEmitter } from "vscode";
2+
import { CancellationToken, Disposable, EventEmitter } from "vscode";
33

4-
export class CommandProcessExecution {
4+
export class CommandProcessExecution implements Disposable {
55
private readonly commandProcess: ChildProcess;
6+
private disposables: Disposable[] = [];
67

7-
constructor(command: string, args?: string[], cwd?: string) {
8+
constructor(
9+
command: string,
10+
args?: string[],
11+
cwd?: string,
12+
token?: CancellationToken
13+
) {
814
this.commandProcess = spawn(command, args, { cwd: cwd });
15+
if (token !== undefined) {
16+
this.disposables.push(token.onCancellationRequested(() => {
17+
this.commandProcess.kill('SIGINT');
18+
}));
19+
}
20+
}
21+
22+
dispose() {
23+
this.disposables.forEach(disposable => disposable.dispose());
924
}
1025

1126
async complete(): Promise<string> {
@@ -34,7 +49,9 @@ export class CommandProcessExecution {
3449
});
3550
}
3651

37-
async completeWithTerminalOutput(writeEmitter: EventEmitter<string>): Promise<void> {
52+
async completeWithTerminalOutput(
53+
writeEmitter: EventEmitter<string>
54+
): Promise<void> {
3855
return new Promise((resolve, reject) => {
3956
this.commandProcess.stdout!.on("data", (chunk) => {
4057
writeEmitter.fire(`\r${this.formatText(chunk.toString())}`);
@@ -43,7 +60,7 @@ export class CommandProcessExecution {
4360
writeEmitter.fire(`\r${this.formatText(chunk.toString())}`);
4461
});
4562
this.commandProcess.once("close", () => {
46-
writeEmitter.fire('\r\n\r\n');
63+
writeEmitter.fire("\r\n\r\n");
4764
resolve();
4865
});
4966

src/dbt_client/dbtClient.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Disposable, EventEmitter, Terminal, window } from "vscode";
1+
import { CancellationToken, Disposable, EventEmitter, Terminal, window } from "vscode";
22
import {
33
OnSourceFileChanged,
44
SourceFileChangedEvent,
@@ -16,7 +16,6 @@ export class DBTClient implements OnSourceFileChanged, Disposable {
1616
private readonly writeEmitter = new EventEmitter<string>();
1717
private readonly queue: DBTCommandQueue = new DBTCommandQueue();
1818
private dbtInstalled?: boolean;
19-
private notYetShownErrorMessage = true;
2019
private terminal?: Terminal;
2120

2221
constructor(pythonPath: string) {
@@ -40,6 +39,7 @@ export class DBTClient implements OnSourceFileChanged, Disposable {
4039
try {
4140
this.raiseDBTInstallationCheckEvent();
4241
await checkDBTInstalledProcess.complete();
42+
checkDBTInstalledProcess.dispose();
4343
} catch (err) {
4444
if (err.match(DBTClient.IS_INSTALLED)) {
4545
this.checkIfDBTIsUpToDate(err);
@@ -51,47 +51,52 @@ export class DBTClient implements OnSourceFileChanged, Disposable {
5151

5252
addCommandToQueue(command: DBTCommand) {
5353
if (!this.dbtInstalled) {
54-
this.notYetShownErrorMessage &&
54+
if (command.focus) {
5555
window.showErrorMessage(
5656
"Please ensure DBT is installed in your selected Python environment."
5757
);
58-
this.notYetShownErrorMessage = false;
58+
}
5959
return;
6060
}
6161

6262
this.queue.addToQueue({
63-
command: () => this.executeCommandImmediately(command),
63+
command: (token) => this.executeCommandImmediately(command, token),
6464
statusMessage: command.statusMessage,
65+
focus: command.focus
6566
});
6667
}
6768

68-
executeCommandImmediately(command: DBTCommand) {
69-
return this.executeCommand(command).completeWithTerminalOutput(
69+
async executeCommandImmediately(command: DBTCommand, token?: CancellationToken) {
70+
const process = this.executeCommand(command, token);
71+
await process.completeWithTerminalOutput(
7072
this.writeEmitter
7173
);
74+
process.dispose();
7275
}
7376

74-
private executeCommand(command: DBTCommand): CommandProcessExecution {
77+
private executeCommand(command: DBTCommand, token?: CancellationToken): CommandProcessExecution {
7578
const { args, cwd } = command.processExecutionParams;
76-
if(this.terminal === undefined) {
79+
if (this.terminal === undefined) {
7780
this.terminal = window.createTerminal({
78-
name: 'DBT',
81+
name: "DBT",
7982
pty: {
8083
onDidWrite: this.writeEmitter.event,
81-
open: () => this.writeEmitter.fire(''),
82-
close: () => {
84+
open: () => this.writeEmitter.fire(""),
85+
close: () => {
8386
this.terminal?.dispose();
8487
this.terminal = undefined;
85-
}
86-
}
88+
},
89+
},
8790
});
8891
}
89-
this.writeEmitter.fire(`\r> Executing task: ${command.commandAsString}\n\r\n\r`);
90-
92+
this.writeEmitter.fire(
93+
`\r> Executing task: ${command.commandAsString}\n\r\n\r`
94+
);
95+
9196
if (command.focus) {
9297
this.terminal.show(true);
9398
}
94-
return new CommandProcessExecution(this.pythonPath, args, cwd);
99+
return new CommandProcessExecution(this.pythonPath, args, cwd, token);
95100
}
96101

97102
private raiseDBTInstallationCheckEvent() {

src/dbt_client/dbtCommandQueue.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { ProgressLocation, window } from "vscode";
1+
import { CancellationToken, ProgressLocation, window } from "vscode";
22

33
interface Command{
4-
command: () => Promise<void>;
4+
command: (token: CancellationToken) => Promise<void>;
55
statusMessage: string;
6+
focus?: boolean;
67
}
78
export class DBTCommandQueue {
89
private queue: Command[] = [];
@@ -16,14 +17,14 @@ export class DBTCommandQueue {
1617
private async pickCommandToRun(): Promise<void> {
1718
if (!this.running && this.queue.length > 0) {
1819
this.running = true;
19-
const { command, statusMessage } = this.queue.shift()!;
20+
const { command, statusMessage, focus } = this.queue.shift()!;
2021

2122
await window.withProgress({
22-
location: ProgressLocation.Window,
23-
cancellable: false,
23+
location: focus ? ProgressLocation.Notification : ProgressLocation.Window,
24+
cancellable: true,
2425
title: statusMessage,
25-
}, async () => {
26-
await command();
26+
}, async (_, token) => {
27+
await command(token);
2728
});
2829

2930
this.running = false;

src/manifest/dbtProjectContainer.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export class DbtProjectContainer implements Disposable {
1919
private manifestCacheChangedHandlers: OnManifestCacheChanged[] = [];
2020
private dbtInstallationFoundHandlers: OnDBTInstallationFound[] = [];
2121
private dbtWorkspaceFolders: DBTWorkspaceFolder[] = [];
22-
private notYetShownDbtInstalledErrorMessage = true;
2322

2423
constructor() {
2524
workspace.onDidChangeWorkspaceFolders(async (event) => {
@@ -89,7 +88,6 @@ export class DbtProjectContainer implements Disposable {
8988
const pythonEnvironment = await PythonEnvironment.getEnvironment();
9089

9190
const pythonPath = pythonEnvironment.getPythonPath();
92-
this.notYetShownDbtInstalledErrorMessage = true;
9391

9492
if (pythonPath === undefined) {
9593
this.dbtClient = undefined;
@@ -114,11 +112,11 @@ export class DbtProjectContainer implements Disposable {
114112

115113
addCommandToQueue(command: DBTCommand) {
116114
if (this.dbtClient === undefined) {
117-
this.notYetShownDbtInstalledErrorMessage &&
115+
if (command.focus) {
118116
window.showErrorMessage(
119-
"Please ensure you have selected a Python interpreter with DBT installed."
117+
"Can't run the command. Please ensure you have selected a Python interpreter with DBT installed."
120118
);
121-
this.notYetShownDbtInstalledErrorMessage = false;
119+
}
122120
return;
123121
}
124122
this.dbtClient.addCommandToQueue(command);
@@ -127,7 +125,7 @@ export class DbtProjectContainer implements Disposable {
127125
async installDBT() {
128126
if (this.dbtClient === undefined) {
129127
window.showErrorMessage(
130-
"Please ensure you have selected a Python interpreter before installing DBT."
128+
"Can't install DBT. Please ensure you have selected a Python interpreter before installing DBT."
131129
);
132130
return;
133131
}
@@ -140,7 +138,7 @@ export class DbtProjectContainer implements Disposable {
140138
async updateDBT() {
141139
if (this.dbtClient === undefined) {
142140
window.showErrorMessage(
143-
"Please ensure you have selected a Python interpreter before updating DBT."
141+
"Can't update DBT. Please ensure you have selected a Python interpreter before updating DBT."
144142
);
145143
return;
146144
}

0 commit comments

Comments
 (0)