Skip to content

Commit

Permalink
Fix for deepcopy errors when running the replica-related logic on Spa…
Browse files Browse the repository at this point in the history
…ces (#5722)

* fix changelogs

* pass

* add changeset

* test

* config

* change

* fixes

* route utils

* add changeset

* add changeset

* add lock

* print

* route url

* replicas

* replicas

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
abidlabs and gradio-pr-bot committed Sep 28, 2023
1 parent 96c4b97 commit dba6519
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/true-candles-pay.md
@@ -0,0 +1,5 @@
---
"gradio": patch
---

feat:Fix for deepcopy errors when running the replica-related logic on Spaces
24 changes: 14 additions & 10 deletions gradio/route_utils.py
@@ -1,6 +1,5 @@
from __future__ import annotations

import copy
import json
from typing import TYPE_CHECKING, Optional, Union

Expand Down Expand Up @@ -248,23 +247,28 @@ async def call_process_api(
return output


def set_replica_url_in_config(config: dict, replica_url: str) -> dict:
def set_replica_url_in_config(
config: dict, replica_url: str, all_replica_urls: set[str]
) -> None:
"""
If the Gradio app is running on Hugging Face Spaces and the machine has multiple replicas,
we pass in the direct URL to the replica so that we have the fully resolved path to any files
on that machine. This direct URL can be shared with other users and the path will still work.
Parameters:
config: The config dictionary to modify.
replica_url: The direct URL to the replica.
all_replica_urls: The direct URLs to the other replicas. These should be replaced with the replica_url.
"""
parsed_url = httpx.URL(replica_url)
stripped_url = parsed_url.copy_with(query=None)
stripped_url = str(stripped_url)
if not stripped_url.endswith("/"):
stripped_url += "/"

config_ = copy.deepcopy(config)
for component in config_["components"]:
if (
component.get("props") is not None
and component["props"].get("root_url") is None
):
component["props"]["root_url"] = stripped_url
return config_
for component in config["components"]:
if component.get("props") is not None:
root_url = component["props"].get("root_url")
# Don't replace the root_url if it's loaded from a different Space
if root_url is None or root_url in all_replica_urls:
component["props"]["root_url"] = stripped_url
10 changes: 6 additions & 4 deletions gradio/routes.py
Expand Up @@ -303,7 +303,7 @@ def login(form_data: OAuth2PasswordRequestForm = Depends()):

@app.head("/", response_class=HTMLResponse)
@app.get("/", response_class=HTMLResponse)
def main(request: fastapi.Request, user: str = Depends(get_current_user)):
async def main(request: fastapi.Request, user: str = Depends(get_current_user)):
mimetypes.add_type("application/javascript", ".js")
blocks = app.get_blocks()
root_path = request.scope.get("root_path", "")
Expand All @@ -317,7 +317,8 @@ def main(request: fastapi.Request, user: str = Depends(get_current_user)):
replica_url = request.headers.get("X-Direct-Url")
if utils.get_space() and replica_url:
app.replica_urls.add(replica_url)
config = set_replica_url_in_config(config, replica_url)
async with app.lock:
set_replica_url_in_config(config, replica_url, app.replica_urls)
else:
config = {
"auth_required": True,
Expand Down Expand Up @@ -354,15 +355,16 @@ def api_info(serialize: bool = True):

@app.get("/config/", dependencies=[Depends(login_check)])
@app.get("/config", dependencies=[Depends(login_check)])
def get_config(request: fastapi.Request):
async def get_config(request: fastapi.Request):
config = app.get_blocks().config

# Handles the case where the app is running on Hugging Face Spaces with
# multiple replicas. See `set_replica_url_in_config` for more details.
replica_url = request.headers.get("X-Direct-Url")
if utils.get_space() and replica_url:
app.replica_urls.add(replica_url)
config = set_replica_url_in_config(config, replica_url)
async with app.lock:
set_replica_url_in_config(config, replica_url, app.replica_urls)

root_path = request.scope.get("root_path", "")
config["root"] = root_path
Expand Down
19 changes: 14 additions & 5 deletions test/test_route_utils.py
Expand Up @@ -3,24 +3,33 @@

def test_set_replica_url():
config = {
"components": [{"props": {}}, {"props": {"root_url": "existing_url/"}}, {}]
"components": [
{"props": {}},
{"props": {"root_url": "existing_url/"}},
{"props": {"root_url": "different_url/"}},
{},
]
}
replica_url = "https://abidlabs-test-client-replica--fttzk.hf.space?__theme=light"

config = set_replica_url_in_config(config, replica_url)
set_replica_url_in_config(config, replica_url, {"existing_url/"})
assert (
config["components"][0]["props"]["root_url"]
== "https://abidlabs-test-client-replica--fttzk.hf.space/"
)
assert config["components"][1]["props"]["root_url"] == "existing_url/"
assert "props" not in config["components"][2]
assert (
config["components"][1]["props"]["root_url"]
== "https://abidlabs-test-client-replica--fttzk.hf.space/"
)
assert config["components"][2]["props"]["root_url"] == "different_url/"
assert "props" not in config["components"][3]


def test_url_without_trailing_slash():
config = {"components": [{"props": {}}]}
replica_url = "https://abidlabs-test-client-replica--fttzk.hf.space"

config = set_replica_url_in_config(config, replica_url)
set_replica_url_in_config(config, replica_url, set())
assert (
config["components"][0]["props"]["root_url"]
== "https://abidlabs-test-client-replica--fttzk.hf.space/"
Expand Down

0 comments on commit dba6519

Please sign in to comment.