Fix functional tests (#6931)

* changes

* add changeset

* add changeset

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* changes

* changes

* changes

---------

Co-authored-by: Ali Abid <aliabid@Alis-MacBook-Pro.local>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
aliabid94 2024-01-04 15:39:16 -08:00 committed by GitHub
parent d361a0f179
commit 6c863af92f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 107 additions and 116 deletions

View File

@ -0,0 +1,9 @@
---
"@gradio/client": patch
"@gradio/fileexplorer": patch
"@gradio/image": patch
"@gradio/upload": patch
"gradio": patch
---
feat:Fix functional tests

View File

@ -820,10 +820,6 @@ export function api_factory(
last_status[fn_index]
);
// TODO: Find out how to print this information
// only during testing
// console.info("data", type, status, data);
if (type == "heartbeat") {
return;
}
@ -844,7 +840,8 @@ export function api_factory(
fire_event({
type: "status",
stage: "error",
message: "An Unexpected Error Occurred!",
message:
status?.message || "An Unexpected Error Occurred!",
queue: true,
endpoint: _endpoint,
fn_index,
@ -1053,6 +1050,17 @@ export function api_factory(
pending_stream_messages[event_id].push(_data);
}
};
event_stream.onerror = async function (event) {
await Promise.all(
Object.keys(event_callbacks).map((event_id) =>
event_callbacks[event_id]({
msg: "unexpected_error",
message: BROKEN_CONNECTION_MSG
})
)
);
close_stream();
};
}
function close_stream(): void {

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import time\n", "import gradio as gr\n", "\n", "\n", "def fake_diffusion(steps):\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " time.sleep(1)\n", " yield str(i)\n", "\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(10)\n", " return 42\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Change\")\n", " cancel_on_submit = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Submit\")\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(sources=[\"webcam\"], label=\"Cancel on clear\", interactive=True)\n", " with gr.Column():\n", " video = gr.Video(sources=[\"webcam\"], label=\"Cancel on start recording\", interactive=True)\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(fn=long_prediction, inputs=[textbox], outputs=prediction)\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event])\n", " image.clear(None, None, None, cancels=[click_event, pred_event])\n", " video.start_recording(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(max_size=20)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import time\n", "import gradio as gr\n", "\n", "\n", "def fake_diffusion(steps):\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " time.sleep(0.2)\n", " yield str(i)\n", "\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(10)\n", " return 42\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Change\")\n", " cancel_on_submit = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Submit\")\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(sources=[\"webcam\"], label=\"Cancel on clear\", interactive=True)\n", " with gr.Column():\n", " video = gr.Video(sources=[\"webcam\"], label=\"Cancel on start recording\", interactive=True)\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(fn=long_prediction, inputs=[textbox], outputs=prediction)\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event])\n", " image.clear(None, None, None, cancels=[click_event, pred_event])\n", " video.start_recording(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(max_size=20)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -5,7 +5,7 @@ import gradio as gr
def fake_diffusion(steps):
for i in range(steps):
print(f"Current step: {i}")
time.sleep(1)
time.sleep(0.2)
yield str(i)

View File

@ -7,10 +7,10 @@ import os
import threading
import urllib.parse
import warnings
from packaging.version import Version
from typing import Any
import httpx
from packaging.version import Version
import gradio
from gradio import wasm_utils

View File

@ -117,7 +117,7 @@ class Block:
self.share_token = secrets.token_urlsafe(32)
self.parent: BlockContext | None = None
self.is_rendered: bool = False
self._constructor_args: dict
self._constructor_args: list[dict]
self.state_session_capacity = 10000
self.temp_files: set[str] = set()
self.GRADIO_CACHE = str(

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import itertools
import os
import re
import warnings
from pathlib import Path
from typing import Any, Callable, List, Literal
@ -39,7 +40,7 @@ class FileExplorer(Component):
*,
value: str | list[str] | Callable | None = None,
file_count: Literal["single", "multiple"] = "multiple",
root: str | Path = ".",
root_dir: str | Path = ".",
ignore_glob: str | None = None,
label: str | None = None,
every: float | None = None,
@ -53,13 +54,14 @@ class FileExplorer(Component):
elem_id: str | None = None,
elem_classes: list[str] | str | None = None,
render: bool = True,
root: None = None,
):
"""
Parameters:
glob: The glob-style pattern used to select which files to display, e.g. "*" to match all files, "*.png" to match all .png files, "**/*.txt" to match any .txt file in any subdirectory, etc. The default value matches all files and folders recursively. See the Python glob documentation at https://docs.python.org/3/library/glob.html for more information.
value: The file (or list of files, depending on the `file_count` parameter) to show as "selected" when the component is first loaded. If a callable is provided, it will be called when the app loads to set the initial value of the component. If not provided, no files are shown as selected.
file_count: Whether to allow single or multiple files to be selected. If "single", the component will return a single absolute file path as a string. If "multiple", the component will return a list of absolute file paths as a list of strings.
root: Path to root directory to select files from. If not provided, defaults to current working directory.
root_dir: Path to root directory to select files from. If not provided, defaults to current working directory.
ignore_glob: The glob-tyle pattern that will be used to exclude files from the list. For example, "*.py" will exclude all .py files from the list. See the Python glob documentation at https://docs.python.org/3/library/glob.html for more information.
label: The label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. Queue must be enabled. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
@ -74,7 +76,13 @@ class FileExplorer(Component):
elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM. Can be used for targeting CSS styles.
render: If False, component will not render be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
"""
self.root = os.path.abspath(root)
if root is not None:
warnings.warn(
"The `root` parameter has been deprecated. Please use `root_dir` instead."
)
root_dir = root
self._constructor_args[0]["root_dir"] = root
self.root_dir = os.path.abspath(root_dir)
self.glob = glob
self.ignore_glob = ignore_glob
valid_file_count = ["single", "multiple", "directory"]
@ -123,8 +131,8 @@ class FileExplorer(Component):
return files
def _strip_root(self, path):
if path.startswith(self.root):
return path[len(self.root) + 1 :]
if path.startswith(self.root_dir):
return path[len(self.root_dir) + 1 :]
return path
def postprocess(self, value: str | list[str] | None) -> FileExplorerData | None:
@ -194,19 +202,19 @@ class FileExplorer(Component):
files: list[Path] = []
for result in expand_braces(self.glob):
files += list(Path(self.root).resolve().glob(result))
files += list(Path(self.root_dir).resolve().glob(result))
files = [f for f in files if f != Path(self.root).resolve()]
files = [f for f in files if f != Path(self.root_dir).resolve()]
ignore_files = []
if self.ignore_glob:
for result in expand_braces(self.ignore_glob):
ignore_files += list(Path(self.root).resolve().glob(result))
ignore_files += list(Path(self.root_dir).resolve().glob(result))
files = list(set(files) - set(ignore_files))
files_with_sep = []
for f in files:
file = str(f.relative_to(self.root))
file = str(f.relative_to(self.root_dir))
if f.is_dir():
file += os.path.sep
files_with_sep.append(file)
@ -215,10 +223,10 @@ class FileExplorer(Component):
return tree
def _safe_join(self, folders):
combined_path = os.path.join(self.root, *folders)
combined_path = os.path.join(self.root_dir, *folders)
absolute_path = os.path.abspath(combined_path)
if os.path.commonprefix([self.root, absolute_path]) != os.path.abspath(
self.root
if os.path.commonprefix([self.root_dir, absolute_path]) != os.path.abspath(
self.root_dir
):
raise ValueError("Attempted to navigate outside of root directory")
return absolute_path

View File

@ -1,72 +0,0 @@
import { test, expect } from "@gradio/tootils";
test.skip("when using an iterative function the UI should update over time as iteration results are received", async ({
page
}) => {
let first_iteration;
let fourth_iteration;
let last_iteration;
const start_button = await page.locator("button", {
hasText: /Start Iterating/
});
const textbox = await page.getByLabel("Iterative Output");
page.on("websocket", (ws) => {
first_iteration = ws.waitForEvent("framereceived", {
predicate: (event) => {
return JSON.parse(event.payload as string).msg === "process_generating";
}
});
fourth_iteration = ws.waitForEvent("framereceived", {
predicate: (event) => {
return JSON.parse(event.payload as string)?.output?.data?.[0] === "3";
}
});
last_iteration = ws.waitForEvent("framereceived", {
predicate: (event) => {
return JSON.parse(event.payload as string).msg === "process_completed";
}
});
});
await start_button.click();
await first_iteration;
await expect(textbox).toHaveValue("0");
await fourth_iteration;
await expect(textbox).toHaveValue("3");
await last_iteration;
await expect(textbox).toHaveValue("8");
});
test.skip("when using an iterative function it should be possible to cancel the function, after which the UI should stop updating", async ({
page
}) => {
let first_iteration;
const start_button = await page.locator("button", {
hasText: /Start Iterating/
});
const stop_button = await page.locator("button", {
hasText: /Stop Iterating/
});
const textbox = await page.getByLabel("Iterative Output");
page.on("websocket", (ws) => {
first_iteration = ws.waitForEvent("framereceived", {
predicate: (event) => {
return JSON.parse(event.payload as string).msg === "process_generating";
}
});
});
await start_button.click();
await first_iteration;
await stop_button.click();
await expect(textbox).toHaveValue("0");
await page.waitForTimeout(1000);
await expect(textbox).toHaveValue("0");
});

View File

@ -0,0 +1,46 @@
import { test, expect } from "@gradio/tootils";
test("when using an iterative function the UI should update over time as iteration results are received", async ({
page
}) => {
const start_button = await page.locator("button", {
hasText: /Start Iterating/
});
const textbox = await page.getByLabel("Iterative Output");
let output_values: string[] = [];
let last_output_value = "";
let interval = setInterval(async () => {
let value = await textbox.inputValue();
if (value !== last_output_value) {
output_values.push(value);
last_output_value = value;
}
}, 100);
await start_button.click();
await expect(textbox).toHaveValue("8");
clearInterval(interval);
for (let i = 1; i < 8; i++) {
expect(output_values).toContain(i.toString());
}
});
test("when using an iterative function it should be possible to cancel the function, after which the UI should stop updating", async ({
page
}) => {
const start_button = await page.locator("button", {
hasText: /Start Iterating/
});
const stop_button = await page.locator("button", {
hasText: /Stop Iterating/
});
const textbox = await page.getByLabel("Iterative Output");
await start_button.click();
await expect(textbox).toHaveValue("0");
await stop_button.click();
await expect(textbox).toHaveValue("0");
await page.waitForTimeout(1000);
await expect(textbox).toHaveValue("0");
});

View File

@ -53,9 +53,7 @@ test("Image drag-to-upload uploads image successfuly.", async ({ page }) => {
await expect(page.getByLabel("# Upload Events")).toHaveValue("1");
});
test.skip("Image copy from clipboard dispatches upload event", async ({
page
}) => {
test("Image copy from clipboard dispatches upload event.", async ({ page }) => {
// Need to make request from inside browser for blob to be formatted correctly
// tried lots of different things
await page.evaluate(async () => {

View File

@ -1,7 +1,7 @@
import { test, expect } from "@gradio/tootils";
import { BASE64_IMAGE } from "./media_data";
test.skip("test inputs", async ({ page }) => {
test("test inputs", async ({ page }) => {
const textbox = await page.getByLabel("Textbox").nth(0);
await expect(textbox).toHaveValue("Lorem ipsum");
@ -34,13 +34,10 @@ test.skip("test inputs", async ({ page }) => {
await expect(image_data_cropper).toContain("cheetah1.jpg");
});
test.skip("test outputs", async ({ page }) => {
test("test outputs", async ({ page }) => {
const submit_button = await page.locator("button", { hasText: /Submit/ });
await Promise.all([
submit_button.click(),
page.waitForResponse("**/run/predict")
]);
await submit_button.click();
const textbox = await page.getByLabel("Textbox").nth(2);
await expect(textbox).toHaveValue(", selected:foo, bar");

View File

@ -1,6 +1,6 @@
import { test, expect } from "@gradio/tootils";
test.skip("UploadButton properly dispatches load event and click event for the single file case.", async ({
test("UploadButton properly dispatches load event and click event for the single file case.", async ({
page
}) => {
await page.getByRole("button", { name: "Upload Single File" }).click();
@ -18,7 +18,7 @@ test.skip("UploadButton properly dispatches load event and click event for the s
await expect(download.suggestedFilename()).toBe("cheetah1.jpg");
});
test.skip("UploadButton properly dispatches load event and click event for the multiple file case.", async ({
test("UploadButton properly dispatches load event and click event for the multiple file case.", async ({
page
}) => {
await page.getByRole("button", { name: "Upload Multiple Files" }).click();

View File

@ -23,7 +23,7 @@
export let glob: string;
export let ignore_glob: string;
export let root: string;
export let root_dir: string;
export let loading_status: LoadingStatus;
export let container = true;
@ -67,7 +67,7 @@
{file_count}
{server}
{interactive}
{root}
{root_dir}
{glob}
{ignore_glob}
on:change={() => gradio.dispatch("change")}

View File

@ -1,6 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import type { Node } from "./utils";
import { dequal } from "dequal";
import FileTree from "./FileTree.svelte";
import { make_fs_store } from "./utils";
@ -9,7 +8,7 @@
export let glob: string;
export let ignore_glob: string;
export let root: string;
export let root_dir: string;
export let interactive: boolean;
export let server: any;
export let file_count: "single" | "multiple" = "multiple";
@ -21,12 +20,13 @@
}>();
const tree = make_fs_store();
$: glob,
ignore_glob,
root,
const render_tree = (): void => {
server.ls().then((v: any) => {
tree.create_fs_graph(v);
});
};
$: glob, ignore_glob, root_dir, render_tree();
$: value.length && $tree && set_checked_from_paths();

View File

@ -65,7 +65,7 @@
}>;
$: url = _value?.url;
$: url && gradio.dispatch("change");
$: url, gradio.dispatch("change");
let dragging: boolean;
let active_source: sources = null;
@ -134,7 +134,6 @@
on:edit={() => gradio.dispatch("edit")}
on:clear={() => {
gradio.dispatch("clear");
gradio.dispatch("change");
}}
on:stream={() => gradio.dispatch("stream")}
on:drag={({ detail }) => (dragging = detail)}

View File

@ -48,14 +48,12 @@
for (let i = 0; i < items.length; i++) {
const type = items[i].types.find((t) => t.startsWith("image/"));
if (type) {
dispatch("load", null);
items[i].getType(type).then(async (blob) => {
const file = new File(
[blob],
`clipboard.${type.replace("image/", "")}`
);
const f = await load_files([file]);
dispatch("load", f?.[0]);
await load_files([file]);
});
break;
}