Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Delete user state when they close the tab. Add an unload event for th…
…e demo and a delete_callback on gr.State to let developers control how resources are cleaned up (#7829) * Delete state * add changeset * Delete state * WIP * Add load event * Working ttl * unload e2e test * Clean up * add changeset * Fix notebook * add changeset * Connect to heartbeat in python client * 15 second heartbeat * Demo for unload * Add notebook * add changeset * Fix docs * revert demo changes * Add docstrings * lint 🙄 * Edit * handle shutdown issue * state comments * client test * Fix: * Fix e2e test * 3.11 incompatibility * delete after one hour * lint + highlight * Update .changeset/better-tires-shave.md Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * Update .changeset/better-tires-shave.md Co-authored-by: Abubakar Abid <abubakar@huggingface.co> --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
- Loading branch information
1 parent
83010a2
commit 6a4bf7a
Showing
21 changed files
with
482 additions
and
45 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,42 @@ | ||
--- | ||
"@gradio/client": minor | ||
"gradio": minor | ||
"gradio_client": minor | ||
--- | ||
|
||
highlight: | ||
|
||
#### Automatically delete state after user has disconnected from the webpage | ||
|
||
Gradio now automatically deletes `gr.State` variables stored in the server's RAM when users close their browser tab. | ||
The deletion will happen 60 minutes after the server detected a disconnect from the user's browser. | ||
If the user connects again in that timeframe, their state will not be deleted. | ||
|
||
Additionally, Gradio now includes a `Blocks.unload()` event, allowing you to run arbitrary cleanup functions when users disconnect (this does not have a 60 minute delay). | ||
You can think of the `unload` event as the opposite of the `load` event. | ||
|
||
|
||
```python | ||
with gr.Blocks() as demo: | ||
gr.Markdown( | ||
"""# State Cleanup Demo | ||
🖼️ Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload. | ||
""") | ||
with gr.Row(): | ||
with gr.Column(scale=1): | ||
with gr.Row(): | ||
img = gr.Image(label="Generated Image", height=300, width=300) | ||
with gr.Row(): | ||
gen = gr.Button(value="Generate") | ||
with gr.Row(): | ||
history = gr.Gallery(label="Previous Generations", height=500, columns=10) | ||
state = gr.State(value=[], delete_callback=lambda v: print("STATE DELETED")) | ||
|
||
demo.load(generate_random_img, [state], [img, state, history]) | ||
gen.click(generate_random_img, [state], [img, state, history]) | ||
demo.unload(delete_directory) | ||
|
||
|
||
demo.launch(auth=lambda user,pwd: True, | ||
auth_message="Enter any username and password to continue") | ||
``` |
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
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
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
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
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
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
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
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 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: state_cleanup"]}, {"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": ["from __future__ import annotations\n", "import gradio as gr\n", "import numpy as np\n", "from PIL import Image\n", "from pathlib import Path\n", "import secrets\n", "import shutil\n", "\n", "current_dir = Path(__file__).parent\n", "\n", "\n", "def generate_random_img(history: list[Image.Image], request: gr.Request):\n", " \"\"\"Generate a random red, green, blue, orange, yellor or purple image.\"\"\"\n", " colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128)]\n", " color = colors[np.random.randint(0, len(colors))]\n", " img = Image.new('RGB', (100, 100), color)\n", " \n", " user_dir: Path = current_dir / request.username # type: ignore\n", " user_dir.mkdir(exist_ok=True)\n", " path = user_dir / f\"{secrets.token_urlsafe(8)}.webp\"\n", "\n", " img.save(path)\n", " history.append(img)\n", "\n", " return img, history, history\n", "\n", "def delete_directory(req: gr.Request):\n", " if not req.username:\n", " return\n", " user_dir: Path = current_dir / req.username\n", " shutil.rmtree(str(user_dir))\n", "\n", "with gr.Blocks() as demo:\n", " gr.Markdown(\"\"\"# State Cleanup Demo\n", " \ud83d\uddbc\ufe0f Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload.\n", " \"\"\")\n", " with gr.Row():\n", " with gr.Column(scale=1):\n", " with gr.Row():\n", " img = gr.Image(label=\"Generated Image\", height=300, width=300)\n", " with gr.Row():\n", " gen = gr.Button(value=\"Generate\")\n", " with gr.Row():\n", " history = gr.Gallery(label=\"Previous Generations\", height=500, columns=10)\n", " state = gr.State(value=[], delete_callback=lambda v: print(\"STATE DELETED\"))\n", "\n", " demo.load(generate_random_img, [state], [img, state, history]) \n", " gen.click(generate_random_img, [state], [img, state, history])\n", " demo.unload(delete_directory)\n", "\n", "\n", "demo.launch(auth=lambda user,pwd: True,\n", " auth_message=\"Enter any username and password to continue\")"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
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,53 @@ | ||
from __future__ import annotations | ||
import gradio as gr | ||
import numpy as np | ||
from PIL import Image | ||
from pathlib import Path | ||
import secrets | ||
import shutil | ||
|
||
current_dir = Path(__file__).parent | ||
|
||
|
||
def generate_random_img(history: list[Image.Image], request: gr.Request): | ||
"""Generate a random red, green, blue, orange, yellor or purple image.""" | ||
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128)] | ||
color = colors[np.random.randint(0, len(colors))] | ||
img = Image.new('RGB', (100, 100), color) | ||
|
||
user_dir: Path = current_dir / request.username # type: ignore | ||
user_dir.mkdir(exist_ok=True) | ||
path = user_dir / f"{secrets.token_urlsafe(8)}.webp" | ||
|
||
img.save(path) | ||
history.append(img) | ||
|
||
return img, history, history | ||
|
||
def delete_directory(req: gr.Request): | ||
if not req.username: | ||
return | ||
user_dir: Path = current_dir / req.username | ||
shutil.rmtree(str(user_dir)) | ||
|
||
with gr.Blocks() as demo: | ||
gr.Markdown("""# State Cleanup Demo | ||
🖼️ Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload. | ||
""") | ||
with gr.Row(): | ||
with gr.Column(scale=1): | ||
with gr.Row(): | ||
img = gr.Image(label="Generated Image", height=300, width=300) | ||
with gr.Row(): | ||
gen = gr.Button(value="Generate") | ||
with gr.Row(): | ||
history = gr.Gallery(label="Previous Generations", height=500, columns=10) | ||
state = gr.State(value=[], delete_callback=lambda v: print("STATE DELETED")) | ||
|
||
demo.load(generate_random_img, [state], [img, state, history]) | ||
gen.click(generate_random_img, [state], [img, state, history]) | ||
demo.unload(delete_directory) | ||
|
||
|
||
demo.launch(auth=lambda user,pwd: True, | ||
auth_message="Enter any username and password to continue") |
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 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: unload_event_test"]}, {"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": ["\"\"\"This demo is only meant to test the unload event.\n", "It will write to a file when the unload event is triggered.\n", "May not work as expected if multiple people are using it.\n", "\"\"\"\n", "import gradio as gr\n", "from pathlib import Path\n", "\n", "log_file = (Path(__file__).parent / \"output_log.txt\").resolve()\n", "\n", "\n", "def test_fn(x):\n", " with open(log_file, \"a\") as f:\n", " f.write(f\"incremented {x}\\n\")\n", " return x + 1, x + 1\n", "\n", "def delete_fn(v):\n", " with log_file.open(\"a\") as f:\n", " f.write(f\"deleted {v}\\n\")\n", "\n", "def unload_fn():\n", " with log_file.open(\"a\") as f:\n", " f.write(f\"unloading\\n\")\n", "\n", "with gr.Blocks() as demo:\n", " n1 = gr.Number(value=0, label=\"Number\")\n", " state = gr.State(value=0, delete_callback=delete_fn)\n", " button = gr.Button(\"Increment\")\n", " button.click(test_fn, [state], [n1, state], api_name=\"increment\")\n", " demo.unload(unload_fn)\n", " demo.load(lambda: log_file.write_text(\"\"))\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
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,33 @@ | ||
"""This demo is only meant to test the unload event. | ||
It will write to a file when the unload event is triggered. | ||
May not work as expected if multiple people are using it. | ||
""" | ||
import gradio as gr | ||
from pathlib import Path | ||
|
||
log_file = (Path(__file__).parent / "output_log.txt").resolve() | ||
|
||
|
||
def test_fn(x): | ||
with open(log_file, "a") as f: | ||
f.write(f"incremented {x}\n") | ||
return x + 1, x + 1 | ||
|
||
def delete_fn(v): | ||
with log_file.open("a") as f: | ||
f.write(f"deleted {v}\n") | ||
|
||
def unload_fn(): | ||
with log_file.open("a") as f: | ||
f.write(f"unloading\n") | ||
|
||
with gr.Blocks() as demo: | ||
n1 = gr.Number(value=0, label="Number") | ||
state = gr.State(value=0, delete_callback=delete_fn) | ||
button = gr.Button("Increment") | ||
button.click(test_fn, [state], [n1, state], api_name="increment") | ||
demo.unload(unload_fn) | ||
demo.load(lambda: log_file.write_text("")) | ||
|
||
if __name__ == "__main__": | ||
demo.launch() |
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
Oops, something went wrong.