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

mount_gradio_app causing reload loop #2427

Closed
1 task done
MarkLambeNitro opened this issue Oct 11, 2022 · 7 comments · Fixed by #2477
Closed
1 task done

mount_gradio_app causing reload loop #2427

MarkLambeNitro opened this issue Oct 11, 2022 · 7 comments · Fixed by #2477
Assignees
Labels
bug Something isn't working

Comments

@MarkLambeNitro
Copy link

MarkLambeNitro commented Oct 11, 2022

Describe the bug

Hello. I'm trying to run some apps using mount_gradio_app. When I go to that page it loads perfectly, but is stuck in a reload loop. In the network tab of the console the /app_id call is being made every ~500ms, it succeeds, gets the same ID returned, but keeps going.

I have a simple page showing at / (read_main) and this works correctly. It is only the sub-APIs that have the reload-loop issue.

Int eh example below / has no issues, but /words (and any other sub API I try) gets caught in this loop.

Any help greatly appreciated. This is for an internal demo that won't be accessible on the internet (But will be hosted in k8s on an internal VPN), so it doesn't need to be full prod ready.

Is there an existing issue for this?

  • I have searched the existing issues

Reproduction

Here's a simple version:

main.py:

#!/usr/bin/env python3
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import gradio as gr
from utils.demos import Demos
import sys

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def read_main():
    return """
    <html>
        <head>
            <title>Demo</title>
        </head>
        <body>
            <div>Demo</div>
        </body>
    </html>
    """

io = gr.Interface(lambda x: "Hello, " + x + "!", "textbox", "textbox")
app = gr.mount_gradio_app(app, demos.get_words_layout(), path="/words")

Dockerfile

# Base Image
FROM python:latest
# set default environment variables
ENV PYTHONUNBUFFERED 1
ENV LANG C.UTF-8
ENV DEBIAN_FRONTEND=noninteractive
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
        tesseract-ocr \
        libtesseract-dev \
        nginx \
        && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# install environment dependencies
COPY ./demos/requirements.txt ./
RUN pip3 install --upgrade pip
RUN pip install -r requirements.txt
RUN ["sh", "-c", "python -m spacy download en_core_web_lg"]

# create and set working directory
RUN mkdir /demos

WORKDIR /demos
COPY ./demos ./

EXPOSE 80
EXPOSE 3000
CMD ["sh", "-c", "service nginx start;uvicorn main:app"]

nginx

server {
    listen 80 default_server;
    server_name myserver;

    location / {
        proxy_pass  http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
}

I'm running the app now with uvicorn main:app but I've tried lots of options, tried it programmatically with reload set explicitly to false, and through Gunicorn and a socket. None of these solve the problem.

Screenshot

image

Logs

Me -> ~/Development/demo -> docker run -p 80:80 -p 3000:3000 -it demo
Starting nginx: nginx.
root@9a8279a9c6cd:/demos# uvicorn main:app
2022-10-11 09:30:54,705 - utils.utils - INFO - log level: INFO
INFO:     Started server process [34]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:52834 - "GET / HTTP/1.0" 200 OK
INFO:     127.0.0.1:52836 - "GET /assets/index.e0c646e0.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52838 - "GET /assets/index.3364fc9e.css HTTP/1.0" 200 OK
INFO:     127.0.0.1:52840 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52842 - "GET /assets/index.da18b6d7.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52850 - "GET /assets/index.cc2d8162.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52854 - "GET /assets/index.7dc02e74.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52856 - "GET /assets/index.5fe10dd6.css HTTP/1.0" 200 OK
INFO:     127.0.0.1:52858 - "GET /assets/index.950ac012.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52862 - "GET /assets/styles.ed3b21b5.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52860 - "GET /assets/Column.6bd7c762.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52868 - "GET /assets/Image.1afdf52a.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52864 - "GET /assets/index.2082796c.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52866 - "GET /assets/BlockLabel.0f912175.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52874 - "GET /assets/Block.19cdfe29.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52872 - "GET /assets/Webcam.955b3879.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52870 - "GET /assets/Upload.c52ab777.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52876 - "GET /assets/ModifyUpload.50f1ed7a.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52878 - "GET /assets/Image.70947e40.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52880 - "GET /assets/index.77f8571c.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52884 - "GET /assets/index.adf51434.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52882 - "GET /assets/csv.27f5436c.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52886 - "GET /assets/dsv.7fe76a93.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52888 - "GET /assets/Model3D.b527a682.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52890 - "GET /assets/logo.1a7cafda.svg HTTP/1.0" 200 OK
INFO:     127.0.0.1:52892 - "GET /assets/index.7bbf77fc.js HTTP/1.0" 200 OK
INFO:     127.0.0.1:52896 - "GET /file%3D/demos/utils/Samples/somethign.jpg HTTP/1.0" 200 OK
INFO:     127.0.0.1:52894 - "GET /file%3D/demos/utils/Samples/image-text.jpg HTTP/1.0" 200 OK
INFO:     127.0.0.1:52898 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52900 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52902 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52904 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52906 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52908 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52910 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52912 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52914 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52916 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52918 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52920 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52922 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52924 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52926 - "GET /app_id HTTP/1.0" 200 OK
INFO:     127.0.0.1:52928 - "GET /app_id HTTP/1.0" 200 OK


...continues forever


with trace:


TRACE:    127.0.0.1:53350 - ASGI [45] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53350 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53350 - ASGI [45] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53350 - ASGI [45] Completed
TRACE:    127.0.0.1:53350 - HTTP connection lost
TRACE:    127.0.0.1:53352 - HTTP connection made
TRACE:    127.0.0.1:53352 - ASGI [46] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 53352), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/words/app_id', 'raw_path': b'/words/app_id', 'query_string': b'', 'headers': '<...>'}
TRACE:    127.0.0.1:53352 - ASGI [46] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53352 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53352 - ASGI [46] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53352 - ASGI [46] Completed
TRACE:    127.0.0.1:53352 - HTTP connection lost
TRACE:    127.0.0.1:53354 - HTTP connection made
TRACE:    127.0.0.1:53354 - ASGI [47] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 53354), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/words/app_id', 'raw_path': b'/words/app_id', 'query_string': b'', 'headers': '<...>'}
TRACE:    127.0.0.1:53354 - ASGI [47] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53354 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53354 - ASGI [47] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53354 - ASGI [47] Completed
TRACE:    127.0.0.1:53354 - HTTP connection lost
TRACE:    127.0.0.1:53356 - HTTP connection made
TRACE:    127.0.0.1:53356 - ASGI [48] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 53356), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/words/app_id', 'raw_path': b'/words/app_id', 'query_string': b'', 'headers': '<...>'}
TRACE:    127.0.0.1:53356 - ASGI [48] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53356 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53356 - ASGI [48] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53356 - ASGI [48] Completed
TRACE:    127.0.0.1:53356 - HTTP connection lost
TRACE:    127.0.0.1:53358 - HTTP connection made
TRACE:    127.0.0.1:53358 - ASGI [49] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 53358), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/words/app_id', 'raw_path': b'/words/app_id', 'query_string': b'', 'headers': '<...>'}
TRACE:    127.0.0.1:53358 - ASGI [49] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53358 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53358 - ASGI [49] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53358 - ASGI [49] Completed
TRACE:    127.0.0.1:53358 - HTTP connection lost
TRACE:    127.0.0.1:53360 - HTTP connection made
TRACE:    127.0.0.1:53360 - ASGI [50] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 53360), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/words/app_id', 'raw_path': b'/words/app_id', 'query_string': b'', 'headers': '<...>'}
TRACE:    127.0.0.1:53360 - ASGI [50] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO:     127.0.0.1:53360 - "GET /app_id HTTP/1.0" 200 OK
TRACE:    127.0.0.1:53360 - ASGI [50] Send {'type': 'http.response.body', 'body': '<30 bytes>'}
TRACE:    127.0.0.1:53360 - ASGI [50] Completed
TRACE:    127.0.0.1:53360 - HTTP connection lost
^CINFO:     Shutting down
INFO:     Waiting for application shutdown.
TRACE:    ASGI [1] Receive {'type': 'lifespan.shutdown'}
TRACE:    ASGI [1] Send {'type': 'lifespan.shutdown.complete'}
TRACE:    ASGI [1] Completed
INFO:     Application shutdown complete.
INFO:     Finished server process [53]


### System Info

```shell
Docker running on Mac, Dockerfile included above.

Severity

blocking all usage of gradio

@MarkLambeNitro MarkLambeNitro added the bug Something isn't working label Oct 11, 2022
@pngwn
Copy link
Member

pngwn commented Oct 11, 2022

It also does this whenever you run gradio run.py ('dev' mode) which makes debugging network requests difficult. It doesn't not cause any issue but it doesn't impair the developer experience. It also doesn't reliably reload the page, so I'm not sure how useful it actually is.

The dev mode functionality should be implemented with websockets rather than polling in this manner.

@freddyaboulton
Copy link
Collaborator

To close this issue, I think we need to make sure mount_gradio_app sets the dev mode to False (currently set to True by default and set to False on launch).

Reimplementing dev mode to not reload the page should be left to another issue.

@freddyaboulton freddyaboulton self-assigned this Oct 11, 2022
@MarkLambeNitro
Copy link
Author

Hey @freddyaboulton , thanks, I had just tracked it down when you commented. I agree with the suggested fix, especially since the behaviour is bizarre and difficult to track down. For now, some updates to the docs might also be helpful. Thanks again!

@felixvor
Copy link

felixvor commented Dec 19, 2022

I am still having this isse.

app = FastAPI()
gradio_interface = gr.Interface(...)
gradio_app = gr.routes.App.create_app(gradio_interface)
app.mount("/", gradio_app)
uvicorn.run(app, host="0.0.0.0", port=7000)

As soon as I open the page my logs are filled with:

...
INFO:     127.0.0.1:47436 - "GET /app_id HTTP/1.1" 200 OK
INFO:     127.0.0.1:47436 - "GET /app_id HTTP/1.1" 200 OK
INFO:     127.0.0.1:47436 - "GET /app_id HTTP/1.1" 200 OK
INFO:     127.0.0.1:47436 - "GET /app_id HTTP/1.1" 200 OK
INFO:     127.0.0.1:47436 - "GET /app_id HTTP/1.1" 200 OK
...

Can you please tell me how to stop that behavior? I use gradio >=3.14.0


Edit:
I was able to fix it by accessing the blocks config after the gradio_app has been created.

app = FastAPI()
gradio_app = gr.routes.App.create_app(gr_interface)
gradio_app.blocks.config["dev_mode"] = False
app.mount("/", gradio_app)
uvicorn.run(app, host="0.0.0.0", port=7000)

@freddyaboulton
Copy link
Collaborator

Hi @DieseKartoffel !

Your workaround does work but you also don't need to manually run App.create_app. You can use mount_gradio_app and just pass in the interface or blocks.

See https://github.com/gradio-app/gradio/blob/main/demo/custom_path/run.py

@x-CK-x
Copy link

x-CK-x commented May 21, 2023

I'm also getting this issue, but only on windows. It loops when calling functions from instantiated objects from other local files with the respective classes.

@JalinWang
Copy link

Hi @DieseKartoffel !

Your workaround does work but you also don't need to manually run App.create_app. You can use mount_gradio_app and just pass in the interface or blocks.

See https://github.com/gradio-app/gradio/blob/main/demo/custom_path/run.py

@freddyaboulton The ray library use create_app where I want to disable infinite app_id requests.

https://github.com/ray-project/ray/blob/c21389f0c0a108b8d33020e26280851c0ac7c372/python/ray/serve/gradio_integrations.py#L21-L31

However, I found it doesn't work to just set dev_mode=False:
image
(It is because __exit__ already generate config before dev_mode is changed)
image

This is confusing and takes me half an hour to figure it out. So I have 2 suggestions for improvement:

  1. hook all the attributes of Block to monitor its change and re-generate config, i.e., implement __setattr__.
  2. Now that most calls to create_app are preceded by get_config_file, why not move get_config_file into create_app?
    image
    image

Look forward to your opinion and I'm glad to make PR if needed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants