mirror of
https://github.com/gradio-app/gradio.git
synced 2025-04-12 12:40:29 +08:00
Lite error handler (#6076)
* Revert "Lite: Error handling after initialization (#6005)" This reverts commit e0ed0642ac2cb4f7ce9ee698d082607d9bab3636. * Lift up the error handler for @gradio/lite to app/src/lite/index.ts and use a new ErrorDisplay component to show the error * add changeset --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
parent
8241f9a7bd
commit
f3f98f923c
7
.changeset/quick-bottles-find.md
Normal file
7
.changeset/quick-bottles-find.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
"@gradio/app": patch
|
||||
"@gradio/wasm": patch
|
||||
"gradio": patch
|
||||
---
|
||||
|
||||
fix:Lite error handler
|
@ -98,29 +98,6 @@
|
||||
worker_proxy.addEventListener("progress-update", (event) => {
|
||||
loading_text = (event as CustomEvent).detail + "...";
|
||||
});
|
||||
worker_proxy.addEventListener("run-start", (event) => {
|
||||
status = {
|
||||
message: "",
|
||||
load_status: "pending",
|
||||
status: "sleeping",
|
||||
detail: "SLEEPING"
|
||||
}
|
||||
});
|
||||
worker_proxy.addEventListener("error", (event) => {
|
||||
const error: Error = (event as CustomEvent).detail;
|
||||
|
||||
// XXX: Although `status` is expected to store Space status info,
|
||||
// we are using it to store the error thrown from the Wasm runtime here
|
||||
// as a workaround to display the error message in the UI
|
||||
// without breaking the existing code.
|
||||
status = {
|
||||
status: "space_error",
|
||||
message: error.message,
|
||||
detail: "RUNTIME_ERROR",
|
||||
load_status: "error",
|
||||
discussions_enabled: false
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export let space: string | null;
|
||||
|
51
js/app/src/lite/ErrorDisplay.svelte
Normal file
51
js/app/src/lite/ErrorDisplay.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { StatusTracker } from "@gradio/statustracker";
|
||||
|
||||
export let is_embed: boolean;
|
||||
export let error: Error | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<StatusTracker
|
||||
absolute={!is_embed}
|
||||
status="error"
|
||||
timer={false}
|
||||
queue_position={null}
|
||||
queue_size={null}
|
||||
translucent={true}
|
||||
>
|
||||
<div class="error" slot="error">
|
||||
{#if error}
|
||||
{#if error.message}
|
||||
<p class="error-name">
|
||||
{error.message}
|
||||
</p>
|
||||
{/if}
|
||||
{#if error.stack}
|
||||
<pre class="error-stack"><code>{error.stack}</code></pre>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</StatusTracker>
|
||||
|
||||
|
||||
<style>
|
||||
.error {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: var(--size-4);
|
||||
color: var(--body-text-color);
|
||||
/* Status tracker sets `pointer-events: none`.
|
||||
Override it here so the user can scroll the element with `overflow: hidden`
|
||||
and copy and paste the error message */
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.error-name {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-stack {
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
@ -1,4 +1,5 @@
|
||||
import "@gradio/theme";
|
||||
import type { SvelteComponent } from "svelte"
|
||||
import { WorkerProxy, type WorkerProxyOptions } from "@gradio/wasm";
|
||||
import { api_factory } from "@gradio/client";
|
||||
import { wasm_proxied_fetch } from "./fetch";
|
||||
@ -6,6 +7,7 @@ import { wasm_proxied_WebSocket_factory } from "./websocket";
|
||||
import { wasm_proxied_mount_css, mount_prebuilt_css } from "./css";
|
||||
import type { mount_css } from "../css";
|
||||
import Index from "../Index.svelte";
|
||||
import ErrorDisplay from "./ErrorDisplay.svelte";
|
||||
import type { ThemeMode } from "../components/types";
|
||||
import { bootstrap_custom_element } from "./custom-element";
|
||||
|
||||
@ -76,14 +78,18 @@ export function create(options: Options): GradioAppController {
|
||||
requirements: options.requirements ?? []
|
||||
});
|
||||
|
||||
worker_proxy.addEventListener("initialization-error", (event) => {
|
||||
showError((event as CustomEvent).detail);
|
||||
});
|
||||
|
||||
// Internally, the execution of `runPythonCode()` or `runPythonFile()` is queued
|
||||
// and its promise will be resolved after the Pyodide is loaded and the worker initialization is done
|
||||
// (see the await in the `onmessage` callback in the webworker code)
|
||||
// So we don't await this promise because we want to mount the `Index` immediately and start the app initialization asynchronously.
|
||||
if (options.code != null) {
|
||||
worker_proxy.runPythonCode(options.code);
|
||||
worker_proxy.runPythonCode(options.code).catch(showError);
|
||||
} else if (options.entrypoint != null) {
|
||||
worker_proxy.runPythonFile(options.entrypoint);
|
||||
worker_proxy.runPythonFile(options.entrypoint).catch(showError)
|
||||
} else {
|
||||
throw new Error("Either code or entrypoint must be provided.");
|
||||
}
|
||||
@ -104,7 +110,20 @@ export function create(options: Options): GradioAppController {
|
||||
return wasm_proxied_mount_css(worker_proxy, url, target);
|
||||
};
|
||||
|
||||
let app: Index;
|
||||
let app: SvelteComponent;
|
||||
function showError(error: Error): void {
|
||||
if (app != null) {
|
||||
app.$destroy();
|
||||
}
|
||||
|
||||
app = new ErrorDisplay({
|
||||
target: options.target,
|
||||
props: {
|
||||
is_embed: !options.isEmbed,
|
||||
error,
|
||||
}
|
||||
});
|
||||
}
|
||||
function launchNewApp(): void {
|
||||
if (app != null) {
|
||||
app.$destroy();
|
||||
@ -144,25 +163,53 @@ export function create(options: Options): GradioAppController {
|
||||
launchNewApp();
|
||||
|
||||
return {
|
||||
run_code: async (code: string): Promise<void> => {
|
||||
await worker_proxy.runPythonCode(code);
|
||||
launchNewApp();
|
||||
run_code: (code: string) => {
|
||||
return worker_proxy.runPythonCode(code)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
run_file: async (path: string): Promise<void> => {
|
||||
await worker_proxy.runPythonFile(path);
|
||||
launchNewApp();
|
||||
run_file: (path: string) => {
|
||||
return worker_proxy.runPythonFile(path)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
write(path, data, opts) {
|
||||
return worker_proxy.writeFile(path, data, opts);
|
||||
write: (path, data, opts) => {
|
||||
return worker_proxy.writeFile(path, data, opts)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
rename(old_path: string, new_path: string): Promise<void> {
|
||||
return worker_proxy.renameFile(old_path, new_path);
|
||||
rename: (old_path, new_path) => {
|
||||
return worker_proxy.renameFile(old_path, new_path)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
unlink(path) {
|
||||
return worker_proxy.unlink(path);
|
||||
unlink: (path) => {
|
||||
return worker_proxy.unlink(path)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
install(requirements) {
|
||||
return worker_proxy.install(requirements);
|
||||
install: (requirements) => {
|
||||
return worker_proxy.install(requirements)
|
||||
.then(launchNewApp)
|
||||
.catch((e) => {
|
||||
showError(e);
|
||||
throw e;
|
||||
})
|
||||
},
|
||||
unmount() {
|
||||
app.$destroy();
|
||||
|
@ -57,7 +57,7 @@ export class WorkerProxy extends EventTarget {
|
||||
error
|
||||
);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
new CustomEvent("initialization-error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
@ -65,43 +65,23 @@ export class WorkerProxy extends EventTarget {
|
||||
}
|
||||
|
||||
public async runPythonCode(code: string): Promise<void> {
|
||||
this.dispatchEvent(new Event("run-start"));
|
||||
try {
|
||||
await this.postMessageAsync({
|
||||
type: "run-python-code",
|
||||
data: {
|
||||
code
|
||||
}
|
||||
});
|
||||
this.firstRunPromiseDelegate.resolve();
|
||||
} catch (error) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
await this.postMessageAsync({
|
||||
type: "run-python-code",
|
||||
data: {
|
||||
code
|
||||
}
|
||||
});
|
||||
this.firstRunPromiseDelegate.resolve();
|
||||
}
|
||||
|
||||
public async runPythonFile(path: string): Promise<void> {
|
||||
this.dispatchEvent(new Event("run-start"));
|
||||
try {
|
||||
await this.postMessageAsync({
|
||||
type: "run-python-file",
|
||||
data: {
|
||||
path
|
||||
}
|
||||
});
|
||||
this.firstRunPromiseDelegate.resolve();
|
||||
} catch (error) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
await this.postMessageAsync({
|
||||
type: "run-python-file",
|
||||
data: {
|
||||
path
|
||||
}
|
||||
});
|
||||
this.firstRunPromiseDelegate.resolve();
|
||||
}
|
||||
|
||||
// A wrapper for this.worker.postMessage(). Unlike that function, which
|
||||
@ -206,13 +186,6 @@ export class WorkerProxy extends EventTarget {
|
||||
data,
|
||||
opts
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}) as Promise<void>;
|
||||
}
|
||||
|
||||
@ -223,13 +196,6 @@ export class WorkerProxy extends EventTarget {
|
||||
oldPath,
|
||||
newPath
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}) as Promise<void>;
|
||||
}
|
||||
|
||||
@ -239,13 +205,6 @@ export class WorkerProxy extends EventTarget {
|
||||
data: {
|
||||
path
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}) as Promise<void>;
|
||||
}
|
||||
|
||||
@ -255,13 +214,6 @@ export class WorkerProxy extends EventTarget {
|
||||
data: {
|
||||
requirements
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("error", {
|
||||
detail: error
|
||||
})
|
||||
);
|
||||
throw error;
|
||||
}) as Promise<void>;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user