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

Programmatically launch reload to allow factories and arguments to be passed to the app #4119

Merged
merged 15 commits into from
May 15, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## New Features:

- The reloader command (`gradio app.py`) can now accept command line arguments by [@micky2be](https://github.com/micky2be) in [PR 4119](https://github.com/gradio-app/gradio/pull/4119)
- Added `format` argument to `Audio` component by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 4178](https://github.com/gradio-app/gradio/pull/4178)
- Add JS client code snippets to use via api page by [@aliabd](https://github.com/aliabd) in [PR 3927](https://github.com/gradio-app/gradio/pull/3927).

Expand Down
53 changes: 44 additions & 9 deletions gradio/reload.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@
Contains the functions that run when `gradio` is called from the command line. Specifically, allows

$ gradio app.py, to run app.py in reload mode where any changes in the app.py file or Gradio library reloads the demo.
$ gradio app.py my_demo, to use variable names other than "demo"
$ gradio app.py my_demo.app, to use variable names other than "demo"
abidlabs marked this conversation as resolved.
Show resolved Hide resolved
"""
import inspect
import os
import sys
from pathlib import Path

from uvicorn import Config
abidlabs marked this conversation as resolved.
Show resolved Hide resolved
from uvicorn.supervisors import ChangeReload

import gradio
from gradio import networking, utils


def run_in_reload_mode():
def _setup_config():
args = sys.argv[1:]
if len(args) == 0:
raise ValueError("No file specified.")
demo_name = "demo" if len(args) == 1 else args[1]
if len(args) == 1 or args[1].startswith("--"):
demo_name = "demo.app"
else:
demo_name = args[1]
if "." not in demo_name:
print(
"WARNING: As of Gradio 3.31, the parameter after the file path must be the name of the FastAPI app, not the Gradio demo. In most cases, this just means you should add '.app' after the name of your demo, e.g. 'demo' -> 'demo.app'."
)

original_path = args[0]
abs_original_path = utils.abspath(original_path)
Expand All @@ -36,21 +46,46 @@ def run_in_reload_mode():
print(
f"\nLaunching in *reload mode* on: http://{networking.LOCALHOST_NAME}:{port} (Press CTRL+C to quit)\n"
)
command = f"uvicorn {filename}:{demo_name}.app --reload --port {port} --log-level warning "
message = "Watching:"

gradio_app = f"{filename}:{demo_name}"
message = "Watching:"
message_change_count = 0

watching_dirs = []
if str(gradio_folder).strip():
command += f'--reload-dir "{gradio_folder}" '
watching_dirs.append(gradio_folder)
message += f" '{gradio_folder}'"
message_change_count += 1

abs_parent = abs_original_path.parent
if str(abs_parent).strip():
command += f'--reload-dir "{abs_parent}"'
watching_dirs.append(abs_parent)
if message_change_count == 1:
message += ","
message += f" '{abs_parent}'"

print(f"{message}\n")
os.system(command)
print(message + "\n")

# guaranty access to the module of an app
sys.path.insert(0, os.getcwd())

# uvicorn.run blocks the execution (looping) which makes it hard to test
return Config(
gradio_app,
reload=True,
port=port,
log_level="warning",
reload_dirs=watching_dirs,
)


def main():
# default execution pattern to start the server and watch changes
config = _setup_config()
server = networking.Server(config)
sock = config.bind_socket()
ChangeReload(config, target=server.run, sockets=[sock]).run()


if __name__ == "__main__":
main()
55 changes: 48 additions & 7 deletions guides/07_other-tutorials/developing-faster-with-reload-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This short Guide will cover both of these methods, so no matter how you write Py

## Python IDE Reload 🔥

If you are building Gradio Blocks using a Python IDE, your file of code (let's name it `app.py`) might look something like this:
If you are building Gradio Blocks using a Python IDE, your file of code (let's name it `run.py`) might look something like this:

```python
import gradio as gr
Expand All @@ -32,11 +32,11 @@ if __name__ == "__main__":
demo.launch()
```

The problem is that anytime that you want to make a change to your layout, events, or components, you have to close and rerun your app by writing `python app.py`.
The problem is that anytime that you want to make a change to your layout, events, or components, you have to close and rerun your app by writing `python run.py`.

Instead of doing this, you can run your code in **reload mode** by changing 1 word: `python` to `gradio`:

In the terminal, run `gradio app.py`. That's it!
In the terminal, run `gradio run.py`. That's it!

Now, you'll see that after you'll see something like this:

Expand All @@ -48,13 +48,54 @@ Watching...
WARNING: The --reload flag should not be used in production on Windows.
```

The important part here is the line that says `Watching...` What's happening here is that Gradio will be observing the directory where `app.py` file lives, and if the file changes, it will automatically rerun the file for you. So you can focus on writing your code, and your Gradio demo will refresh automatically 🥳
The important part here is the line that says `Watching...` What's happening here is that Gradio will be observing the directory where `run.py` file lives, and if the file changes, it will automatically rerun the file for you. So you can focus on writing your code, and your Gradio demo will refresh automatically 🥳

⚠️ Now, there is one important thing to keep in mind when using the reload mode: Gradio specifically looks for a Gradio Blocks/Interface demo called `demo` in your code. If you have named your demo something else, you can pass that as the 2nd parameter in your code, like this: `gradio app.py my_demo`
⚠️ Warning: the `gradio` command does not detect the parameters passed to the `launch()` methods because the `launch()` method is never called in reload mode. For example, setting `auth`, or `show_error` in `launch()` will not be reflected in the app.

As a small aside, this auto-reloading happens if you change your `app.py` source code or the Gradio source code. Meaning that this can be useful if you decide to [contribute to Gradio itself](https://github.com/gradio-app/gradio/blob/main/CONTRIBUTING.md) ✅
There is one important thing to keep in mind when using the reload mode: Gradio specifically looks for a Gradio Blocks/Interface demo called `demo` in your code. If you have named your demo something else, you will need to pass in the name of your demo's FastAPI app as the 2nd parameter in your code. For Gradio demos, the FastAPI app can be accessed using the `.app` attribute. So if your `run.py` file looked like this:

⚠️ The `gradio` command will not detect the parameters passed to the `launch()` methods. For example, setting `auth`, or `show_error` in `launch()` will not be reflected in the app.
```python
import gradio as gr

with gr.Blocks() as my_demo:
gr.Markdown("# Greetings from Gradio!")
inp = gr.Textbox(placeholder="What is your name?")
out = gr.Textbox()

inp.change(fn=lambda x: f"Welcome, {x}!",
inputs=inp,
outputs=out)

if __name__ == "__main__":
my_demo.launch()
```

Then you would launch it in reload mode like this: `gradio run.py my_demo.app`.

🔥 If your application accepts command line arguments, you can pass them in as well. Here's an example:

```python
import gradio as gr
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--name", type=str, default="User")
args, unknown = parser.parse_known_args()

with gr.Blocks() as demo:
gr.Markdown(f"# Greetings {args.name}!")
inp = gr.Textbox()
out = gr.Textbox()

inp.change(fn=lambda x: x, inputs=inp, outputs=out)

if __name__ == "__main__":
demo.launch()
```

Which you could run like this: `gradio run.py --name Gretel`

As a small aside, this auto-reloading happens if you change your `run.py` source code or the Gradio source code. Meaning that this can be useful if you decide to [contribute to Gradio itself](https://github.com/gradio-app/gradio/blob/main/CONTRIBUTING.md) ✅

## Jupyter Notebook Magic 🔮

Expand Down
Loading