-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Execute] Revisit executeQuickInput file (#971)
* [Execute] Revisit executeQuickInput file Let's revisit executeQuickInput file. ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <[email protected]> * Revise more ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <[email protected]> * Introduce InferenceQuickInput, InferenceRunner and InferenceShowBox ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>> * Prepare tests ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>> * fix ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>> * Fill tests of InferenceQuickInput ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>> * Fill tests of InferenceRunner ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>> * Revert commandPalette option ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>
- Loading branch information
1 parent
d9739d3
commit 0f0530d
Showing
7 changed files
with
604 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/* | ||
* Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import * as vscode from 'vscode'; | ||
|
||
import {Backend} from '../Backend/API'; | ||
import {globalBackendMap} from '../Backend/Backend'; | ||
import {Executor} from '../Backend/Executor'; | ||
import {Logger} from '../Utils/Logger'; | ||
import {MultiStepInput} from '../Utils/MultiStepInput'; | ||
|
||
const logTag = 'InferenceQuickInput'; | ||
|
||
interface State { | ||
selectedItem: vscode.QuickPickItem; | ||
} | ||
|
||
class InferenceQuickInput { | ||
backend: Backend|undefined = undefined; | ||
modelPath: vscode.Uri|undefined = undefined; | ||
inputSpec: string|undefined = undefined; | ||
error: string|undefined = undefined; | ||
|
||
constructor() {} | ||
|
||
getBackend(): Backend { | ||
if (this.error !== undefined || this.backend === undefined) { | ||
throw new Error('wrong calling'); | ||
} | ||
return this.backend as Backend; | ||
} | ||
|
||
getModelPath(): vscode.Uri { | ||
if (this.error !== undefined || this.modelPath === undefined) { | ||
throw new Error('wrong calling'); | ||
} | ||
return this.modelPath as vscode.Uri; | ||
} | ||
|
||
getInputSpec(): string { | ||
if (this.error !== undefined || this.inputSpec === undefined) { | ||
throw new Error('wrong calling'); | ||
} | ||
return this.inputSpec as string; | ||
} | ||
|
||
getError(): string|undefined { | ||
return this.error; | ||
} | ||
|
||
async shouldResume(): Promise<boolean> { | ||
// Could show a notification with the option to resume. | ||
return new Promise<boolean>( | ||
(resolve, reject) => { | ||
// noop | ||
}); | ||
} | ||
|
||
getAllBackendNames(): string[] { | ||
return Object.keys(globalBackendMap); | ||
} | ||
|
||
getQuickPickItems(items: string[]): vscode.QuickPickItem[] { | ||
return items.map(label => ({label})); | ||
} | ||
|
||
getBackendFromGlobal(key: string): Backend { | ||
return globalBackendMap[key]; | ||
} | ||
|
||
async pickBackend(input: MultiStepInput, state: Partial<State>) { | ||
const items = this.getQuickPickItems(this.getAllBackendNames()); | ||
state.selectedItem = await input.showQuickPick({ | ||
title: 'Choose Executor Toolchain', | ||
step: 1, | ||
totalSteps: 3, | ||
placeholder: 'Select a Backend', | ||
items: items, | ||
shouldResume: this.shouldResume | ||
}); | ||
|
||
this.backend = this.getBackendFromGlobal(state.selectedItem.label); | ||
|
||
if (this.backend === undefined) { | ||
this.error = 'Backend to infer is not chosen. Please check once again.'; | ||
return undefined; | ||
} | ||
if (this.backend.executor() === undefined) { | ||
this.error = 'Backend executor is not set yet. Please check once again.'; | ||
return undefined; | ||
} | ||
} | ||
|
||
getFilter(): {[name: string]: string[]} { | ||
const backend: Backend = this.backend as Backend; | ||
const executor = backend.executor() as Executor; | ||
// List files which are filtered with executable extensions | ||
return {backendName: executor.getExecutableExt()}; | ||
} | ||
|
||
// TODO: Use quickPick window with fast grep child process | ||
async selectInputModel(input: MultiStepInput, state: Partial<State>) { | ||
const filter = this.getFilter(); | ||
|
||
const fileUri = await vscode.window.showOpenDialog({ | ||
title: 'Select Model to Infer', | ||
canSelectMany: false, | ||
openLabel: 'Select Model to Infer', | ||
filters: filter | ||
}); | ||
|
||
if (fileUri && fileUri[0]) { | ||
this.modelPath = fileUri[0]; | ||
return; | ||
} | ||
|
||
Logger.warn(logTag, 'No model has been selected'); | ||
this.error = 'No model has been selected. Please check once again.'; | ||
return undefined; | ||
} | ||
|
||
getInputSpecKeys(): string[] { | ||
return ['any', 'non-zero', 'positive']; | ||
} | ||
|
||
// TODO: enable the backend-driven option steps by backend extension | ||
async selectInputSpec(input: MultiStepInput, state: Partial<State>): Promise<void> { | ||
const items = this.getQuickPickItems(this.getInputSpecKeys()); | ||
state.selectedItem = await input.showQuickPick({ | ||
title: 'Enter Backend Specific Options', | ||
step: 3, | ||
totalSteps: 3, | ||
placeholder: 'Select Random Input Spec', | ||
items: items, | ||
shouldResume: this.shouldResume | ||
}); | ||
this.inputSpec = state.selectedItem.label; | ||
} | ||
|
||
async collectInputs(): Promise<void> { | ||
const state = {selectedItem: undefined} as Partial<State>; | ||
await MultiStepInput.run(input => this.pickBackend(input, state)); | ||
await MultiStepInput.run(input => this.selectInputModel(input, state)); | ||
await MultiStepInput.run(input => this.selectInputSpec(input, state)); | ||
} | ||
} | ||
|
||
export {State, InferenceQuickInput}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {exec} from 'child_process'; | ||
import {appendFileSync} from 'fs'; | ||
import * as vscode from 'vscode'; | ||
|
||
import {Backend} from '../Backend/API'; | ||
import {Command} from '../Backend/Command'; | ||
import {Executor} from '../Backend/Executor'; | ||
import {Logger} from '../Utils/Logger'; | ||
|
||
import {InfernceShowBox, Task} from './InferenceShowBox'; | ||
|
||
const logTag = 'InferenceRunner'; | ||
|
||
class InferenceRunner { | ||
backend: Backend; | ||
modelPath: vscode.Uri; | ||
inputSpec: string; | ||
|
||
constructor(backend: Backend, modelPath: vscode.Uri, inputSpec: string) { | ||
this.backend = backend; | ||
this.modelPath = modelPath; | ||
this.inputSpec = inputSpec; | ||
} | ||
|
||
getInferenceCmd(): Command { | ||
const executor = this.backend.executor() as Executor; | ||
return executor.runInference(this.modelPath.path, ['--input-spec', this.inputSpec]); | ||
} | ||
|
||
getOutFileName(): string { | ||
return `${this.modelPath.path}.infer.log`; | ||
} | ||
|
||
// TODO: It's possible divide to inferance and others | ||
getInferenceTask(cmd: Command, outFileName: string): Task { | ||
return (resolve, reject) => { | ||
exec(cmd.str() + ' > ' + outFileName, (error, stdout, stderr) => { | ||
if (error) { | ||
appendFileSync(outFileName, error.message); | ||
return reject(error.message); | ||
} | ||
// Some of warnings are treated as error. | ||
// TODO: handle the stderr | ||
// if (stderr) return reject(stderr); | ||
else { | ||
return resolve(stdout); | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
async runInference(): Promise<void> { | ||
const cmd = this.getInferenceCmd(); | ||
const outFileName = this.getOutFileName(); | ||
const task = this.getInferenceTask(cmd, outFileName); | ||
|
||
const showBox = new InfernceShowBox(); | ||
await showBox.showInference( | ||
task, `Inference succeeded! You can find the log at ${outFileName}`, | ||
`Exception Occurred! You can find the log at ${outFileName}`); | ||
} | ||
} | ||
|
||
export {InferenceRunner}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import {Logger} from '../Utils/Logger'; | ||
|
||
type Task = (resolve: (value: unknown) => void, reject: (reason?: any) => void) => unknown; | ||
|
||
const logTag = 'InfernceShowBox'; | ||
|
||
class InfernceShowBox { | ||
constructor() {} | ||
|
||
private async showProgressWith(task: Task): Promise<void> { | ||
await vscode.window.withProgress( | ||
{ | ||
location: vscode.ProgressLocation.Notification, | ||
title: 'Inference Running!', | ||
cancellable: true | ||
}, | ||
(progress, token) => { | ||
token.onCancellationRequested(() => { | ||
Logger.info(logTag, 'User canceled the log running operation'); | ||
}); | ||
return new Promise((resolve, reject) => { | ||
return task(resolve, reject); | ||
}); | ||
}); | ||
} | ||
|
||
async showInference(task: Task, successMsg: string, failedMsg: string) { | ||
this.showProgressWith(task).then( | ||
() => vscode.window.showInformationMessage(successMsg, {title: 'OK'})), | ||
// TODO: Find if the message includes new line character. | ||
() => vscode.window.showErrorMessage(failedMsg, {title: 'Close'}); | ||
} | ||
} | ||
|
||
export {Task, InfernceShowBox}; |
Oops, something went wrong.