Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add delete event to File component #8417

Merged
merged 14 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/full-flies-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@gradio/file": minor
"@gradio/tootils": minor
"@gradio/upload": minor
"gradio": minor
---

feat:add delete event to `File` component
2 changes: 1 addition & 1 deletion demo/file_component_events/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: file_component_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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " \n", " with gr.Row():\n", " with gr.Column():\n", " file_component = gr.File(label=\"Upload Single File\", file_count=\"single\")\n", " with gr.Column():\n", " output_file_1 = gr.File(label=\"Upload Single File Output\", file_count=\"single\")\n", " num_load_btn_1 = gr.Number(label=\"# Load Upload Single File\", value=0)\n", " file_component.upload(lambda s,n: (s, n + 1), [file_component, num_load_btn_1], [output_file_1, num_load_btn_1])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_multiple = gr.File(label=\"Upload Multiple Files\", file_count=\"multiple\")\n", " with gr.Column():\n", " output_file_2 = gr.File(label=\"Upload Multiple Files Output\", file_count=\"multiple\")\n", " num_load_btn_2 = gr.Number(label=\"# Load Upload Multiple Files\", value=0)\n", " file_component_multiple.upload(lambda s,n: (s, n + 1), [file_component_multiple, num_load_btn_2], [output_file_2, num_load_btn_2])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_specific = gr.File(label=\"Upload Multiple Files Image/Video\", file_count=\"multiple\", file_types=[\"image\", \"video\"])\n", " with gr.Column():\n", " output_file_3 = gr.File(label=\"Upload Multiple Files Output Image/Video\", file_count=\"multiple\")\n", " num_load_btn_3 = gr.Number(label=\"# Load Upload Multiple Files Image/Video\", value=0)\n", " file_component_specific.upload(lambda s,n: (s, n + 1), [file_component_specific, num_load_btn_3], [output_file_3, num_load_btn_3])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_pdf = gr.File(label=\"Upload PDF File\", file_types=[\"pdf\"])\n", " with gr.Column():\n", " output_file_4 = gr.File(label=\"Upload PDF File Output\")\n", " num_load_btn_4 = gr.Number(label=\"# Load Upload PDF File\", value=0)\n", " file_component_pdf.upload(lambda s,n: (s, n + 1), [file_component_pdf, num_load_btn_4], [output_file_4, num_load_btn_4])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_invalid = gr.File(label=\"Upload File with Invalid file_types\", file_types=[\"invalid file_type\"])\n", " with gr.Column():\n", " output_file_5 = gr.File(label=\"Upload File with Invalid file_types Output\")\n", " num_load_btn_5 = gr.Number(label=\"# Load Upload File with Invalid file_types\", value=0)\n", " file_component_invalid.upload(lambda s,n: (s, n + 1), [file_component_invalid, num_load_btn_5], [output_file_5, num_load_btn_5])\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: file_component_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 gradio as gr\n", "\n", "\n", "def delete_file(n: int, file: gr.DeletedFileData):\n", " return [file.file.path, n + 1]\n", "\n", "\n", "with gr.Blocks() as demo:\n", "\n", " with gr.Row():\n", " with gr.Column():\n", " file_component = gr.File(label=\"Upload Single File\", file_count=\"single\")\n", " with gr.Column():\n", " output_file_1 = gr.File(\n", " label=\"Upload Single File Output\", file_count=\"single\"\n", " )\n", " num_load_btn_1 = gr.Number(label=\"# Load Upload Single File\", value=0)\n", " file_component.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component, num_load_btn_1],\n", " [output_file_1, num_load_btn_1],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_multiple = gr.File(\n", " label=\"Upload Multiple Files\", file_count=\"multiple\"\n", " )\n", " with gr.Column():\n", " output_file_2 = gr.File(\n", " label=\"Upload Multiple Files Output\", file_count=\"multiple\"\n", " )\n", " num_load_btn_2 = gr.Number(label=\"# Load Upload Multiple Files\", value=0)\n", " file_component_multiple.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_multiple, num_load_btn_2],\n", " [output_file_2, num_load_btn_2],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_specific = gr.File(\n", " label=\"Upload Multiple Files Image/Video\",\n", " file_count=\"multiple\",\n", " file_types=[\"image\", \"video\"],\n", " )\n", " with gr.Column():\n", " output_file_3 = gr.File(\n", " label=\"Upload Multiple Files Output Image/Video\", file_count=\"multiple\"\n", " )\n", " num_load_btn_3 = gr.Number(\n", " label=\"# Load Upload Multiple Files Image/Video\", value=0\n", " )\n", " file_component_specific.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_specific, num_load_btn_3],\n", " [output_file_3, num_load_btn_3],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_pdf = gr.File(label=\"Upload PDF File\", file_types=[\"pdf\"])\n", " with gr.Column():\n", " output_file_4 = gr.File(label=\"Upload PDF File Output\")\n", " num_load_btn_4 = gr.Number(label=\"# Load Upload PDF File\", value=0)\n", " file_component_pdf.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_pdf, num_load_btn_4],\n", " [output_file_4, num_load_btn_4],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_invalid = gr.File(\n", " label=\"Upload File with Invalid file_types\",\n", " file_types=[\"invalid file_type\"],\n", " )\n", " with gr.Column():\n", " output_file_5 = gr.File(label=\"Upload File with Invalid file_types Output\")\n", " num_load_btn_5 = gr.Number(\n", " label=\"# Load Upload File with Invalid file_types\", value=0\n", " )\n", " file_component_invalid.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_invalid, num_load_btn_5],\n", " [output_file_5, num_load_btn_5],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " del_file_input = gr.File(label=\"Delete File\", file_count=\"multiple\")\n", " with gr.Column():\n", " del_file_data = gr.Textbox(label=\"Delete file data\")\n", " num_load_btn_6 = gr.Number(label=\"# Deleted File\", value=0)\n", " del_file_input.delete(\n", " delete_file,\n", " [num_load_btn_6],\n", " [del_file_data, num_load_btn_6],\n", " )\n", " # f = gr.File(label=\"Upload many File\", file_count=\"multiple\")\n", " # # f.delete(delete_file)\n", " # f.delete(delete_file, inputs=None, outputs=None)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
86 changes: 72 additions & 14 deletions demo/file_component_events/run.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,100 @@
import gradio as gr


def delete_file(n: int, file: gr.DeletedFileData):
return [file.file.path, n + 1]


with gr.Blocks() as demo:

with gr.Row():
with gr.Column():
file_component = gr.File(label="Upload Single File", file_count="single")
with gr.Column():
output_file_1 = gr.File(label="Upload Single File Output", file_count="single")
output_file_1 = gr.File(
label="Upload Single File Output", file_count="single"
)
num_load_btn_1 = gr.Number(label="# Load Upload Single File", value=0)
file_component.upload(lambda s,n: (s, n + 1), [file_component, num_load_btn_1], [output_file_1, num_load_btn_1])
file_component.upload(
lambda s, n: (s, n + 1),
[file_component, num_load_btn_1],
[output_file_1, num_load_btn_1],
)
with gr.Row():
with gr.Column():
file_component_multiple = gr.File(label="Upload Multiple Files", file_count="multiple")
file_component_multiple = gr.File(
label="Upload Multiple Files", file_count="multiple"
)
with gr.Column():
output_file_2 = gr.File(label="Upload Multiple Files Output", file_count="multiple")
output_file_2 = gr.File(
label="Upload Multiple Files Output", file_count="multiple"
)
num_load_btn_2 = gr.Number(label="# Load Upload Multiple Files", value=0)
file_component_multiple.upload(lambda s,n: (s, n + 1), [file_component_multiple, num_load_btn_2], [output_file_2, num_load_btn_2])
file_component_multiple.upload(
lambda s, n: (s, n + 1),
[file_component_multiple, num_load_btn_2],
[output_file_2, num_load_btn_2],
)
with gr.Row():
with gr.Column():
file_component_specific = gr.File(label="Upload Multiple Files Image/Video", file_count="multiple", file_types=["image", "video"])
file_component_specific = gr.File(
label="Upload Multiple Files Image/Video",
file_count="multiple",
file_types=["image", "video"],
)
with gr.Column():
output_file_3 = gr.File(label="Upload Multiple Files Output Image/Video", file_count="multiple")
num_load_btn_3 = gr.Number(label="# Load Upload Multiple Files Image/Video", value=0)
file_component_specific.upload(lambda s,n: (s, n + 1), [file_component_specific, num_load_btn_3], [output_file_3, num_load_btn_3])
output_file_3 = gr.File(
label="Upload Multiple Files Output Image/Video", file_count="multiple"
)
num_load_btn_3 = gr.Number(
label="# Load Upload Multiple Files Image/Video", value=0
)
file_component_specific.upload(
lambda s, n: (s, n + 1),
[file_component_specific, num_load_btn_3],
[output_file_3, num_load_btn_3],
)
with gr.Row():
with gr.Column():
file_component_pdf = gr.File(label="Upload PDF File", file_types=["pdf"])
with gr.Column():
output_file_4 = gr.File(label="Upload PDF File Output")
num_load_btn_4 = gr.Number(label="# Load Upload PDF File", value=0)
file_component_pdf.upload(lambda s,n: (s, n + 1), [file_component_pdf, num_load_btn_4], [output_file_4, num_load_btn_4])
file_component_pdf.upload(
lambda s, n: (s, n + 1),
[file_component_pdf, num_load_btn_4],
[output_file_4, num_load_btn_4],
)
with gr.Row():
with gr.Column():
file_component_invalid = gr.File(label="Upload File with Invalid file_types", file_types=["invalid file_type"])
file_component_invalid = gr.File(
label="Upload File with Invalid file_types",
file_types=["invalid file_type"],
)
with gr.Column():
output_file_5 = gr.File(label="Upload File with Invalid file_types Output")
num_load_btn_5 = gr.Number(label="# Load Upload File with Invalid file_types", value=0)
file_component_invalid.upload(lambda s,n: (s, n + 1), [file_component_invalid, num_load_btn_5], [output_file_5, num_load_btn_5])
num_load_btn_5 = gr.Number(
label="# Load Upload File with Invalid file_types", value=0
)
file_component_invalid.upload(
lambda s, n: (s, n + 1),
[file_component_invalid, num_load_btn_5],
[output_file_5, num_load_btn_5],
)
with gr.Row():
with gr.Column():
del_file_input = gr.File(label="Delete File", file_count="multiple")
with gr.Column():
del_file_data = gr.Textbox(label="Delete file data")
num_load_btn_6 = gr.Number(label="# Deleted File", value=0)
del_file_input.delete(
delete_file,
[num_load_btn_6],
[del_file_data, num_load_btn_6],
)
# f = gr.File(label="Upload many File", file_count="multiple")
# # f.delete(delete_file)
# f.delete(delete_file, inputs=None, outputs=None)

if __name__ == "__main__":
demo.launch()
9 changes: 8 additions & 1 deletion gradio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@
from gradio.components.audio import WaveformOptions
from gradio.components.image_editor import Brush, Eraser
from gradio.data_classes import FileData
from gradio.events import EventData, KeyUpData, LikeData, SelectData, on
from gradio.events import (
DeletedFileData,
EventData,
KeyUpData,
LikeData,
SelectData,
on,
)
from gradio.exceptions import Error
from gradio.external import load
from gradio.flagging import (
Expand Down
2 changes: 1 addition & 1 deletion gradio/components/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class File(Component):
Demo: zip_files, zip_to_json
"""

EVENTS = [Events.change, Events.select, Events.clear, Events.upload]
EVENTS = [Events.change, Events.select, Events.clear, Events.upload, Events.delete]

def __init__(
self,
Expand Down
12 changes: 11 additions & 1 deletion gradio/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import shutil
from abc import ABC, abstractmethod
from enum import Enum, auto
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, TypedDict, Union

from fastapi import Request
from gradio_client.utils import traverse
Expand Down Expand Up @@ -189,6 +189,16 @@ def from_json(cls, x) -> GradioRootModel:
GradioDataModel = Union[GradioModel, GradioRootModel]


class FileDataDict(TypedDict):
path: str # server filepath
url: Optional[str] # normalised server url
size: Optional[int] # size in bytes
orig_name: Optional[str] # original filename
mime_type: Optional[str]
is_stream: bool
meta: dict
Comment on lines +192 to +199
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so I could type my Event data correctly



class FileData(GradioModel):
path: str # server filepath
url: Optional[str] = None # normalised server url
Expand Down
15 changes: 15 additions & 0 deletions gradio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from gradio_client.documentation import document
from jinja2 import Template

from gradio.data_classes import FileData, FileDataDict

if TYPE_CHECKING:
from gradio.blocks import Block, Component

Expand Down Expand Up @@ -149,6 +151,15 @@ def __init__(self, target: Block | None, data: Any):
"""


class DeletedFileData(EventData):
def __init__(self, target: Block | None, data: FileDataDict):
super().__init__(target, data)
self.file: FileData = FileData(**data)
"""
The file that was deleted.
"""
Comment on lines +154 to +160
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New EventData type, carries the deleted file as an instance of FileData



@dataclasses.dataclass
class EventListenerMethod:
block: Block | None
Expand Down Expand Up @@ -585,6 +596,10 @@ class Events:
"apply",
doc="This listener is triggered when the user applies changes to the {{ component }} through an integrated UI action.",
)
delete = EventListener(
"delete",
doc="This listener is triggered when the user deletes and item from the {{ component }}. Uses event data gradio.DeletedFileData to carry `value` referring to the file that was deleted as an instance of FileData. See EventData documentation on how to use this event data",
)


class LikeData(EventData):
Expand Down
42 changes: 36 additions & 6 deletions js/app/test/file_component_events.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { test, expect, drag_and_drop_file } from "@gradio/tootils";

async function error_modal_showed(page) {
const toast = page.getByTestId("toast-body");
expect(toast).toContainText("error");
const close = page.getByTestId("toast-close");
await close.click();
await expect(page.getByTestId("toast-body")).toHaveCount(0);
}

test("File component properly dispatches load event for the single file case.", async ({
page
}) => {
Expand Down Expand Up @@ -63,11 +71,33 @@ test("File component properly handles drag and drop of pdf file.", async ({
test("File component properly handles invalid file_types.", async ({
page
}) => {
const uploader = await page.locator("input[type=file]").last();
await uploader.setInputFiles(["./test/files/cheetah1.jpg"]);
const locator = page.locator("input[type=file]").nth(4);
await drag_and_drop_file(
page,
locator,
"./test/files/cheetah1.jpg",
"cheetah1.jpg",
"image/jpeg"
);

// Check that the pdf file was uploaded
await expect(
page.getByLabel("# Load Upload File with Invalid file_types")
).toHaveValue("1");
await error_modal_showed(page);
});

test("Delete event is fired correctly", async ({ page }) => {
const locator = page.locator("input[type=file]").nth(5);
await drag_and_drop_file(
page,
locator,
"./test/files/cheetah1.jpg",
"cheetah1.jpg",
"image/jpeg",
2
);

await page.getByLabel("Remove this file").first().click();

await expect(page.getByLabel("# Deleted File")).toHaveValue("1");
expect(
(await page.getByLabel("Delete file data").inputValue()).length
).toBeGreaterThan(5);
});
4 changes: 4 additions & 0 deletions js/file/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
clear: never;
select: SelectData;
clear_status: LoadingStatus;
delete: FileData;
}>;
export let file_count: string;
export let file_types: string[] = ["file"];
Expand Down Expand Up @@ -110,6 +111,9 @@
loading_status.status = "error";
gradio.dispatch("error", detail);
}}
on:delete={({ detail }) => {
gradio.dispatch("delete", detail);
}}
i18n={gradio.i18n}
>
<UploadText i18n={gradio.i18n} type="file" />
Expand Down
4 changes: 3 additions & 1 deletion js/file/shared/FilePreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
const dispatch = createEventDispatcher<{
select: SelectData;
change: FileData[] | FileData;
delete: FileData;
}>();
export let value: FileData | FileData[];
export let selectable = false;
Expand Down Expand Up @@ -46,9 +47,10 @@
}

function remove_file(index: number): void {
normalized_files.splice(index, 1);
const removed = normalized_files.splice(index, 1);
normalized_files = [...normalized_files];
value = normalized_files;
dispatch("delete", removed[0]);
dispatch("change", normalized_files);
}
</script>
Expand Down
17 changes: 10 additions & 7 deletions js/file/shared/FileUpload.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@
$: dispatch("drag", dragging);
</script>

<BlockLabel
{show_label}
Icon={File}
float={value === null}
label={label || "File"}
/>
<BlockLabel {show_label} Icon={File} float={!value} label={label || "File"} />

{#if value && (Array.isArray(value) ? value.length > 0 : true)}
<ModifyUpload {i18n} on:clear={handle_clear} absolute />
<FilePreview {i18n} on:select {selectable} {value} {height} on:change />
<FilePreview
{i18n}
on:select
{selectable}
{value}
{height}
on:change
on:delete
/>
{:else}
<Upload
on:load={handle_upload}
Expand Down
Loading