Skip to content

Commit

Permalink
Programmatically launch reload to allow factories and arguments to be…
Browse files Browse the repository at this point in the history
… passed to the app (#4119)

* Programmatically lauch reload to allow factories and arguments to be passed to the app

* add warning

* modify warning

* guide fixes

* formatting

* changes

* warning

* import fix

* fix tests

* fix tests

---------

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
  • Loading branch information
2 people authored and dawoodkhan82 committed Jun 2, 2023
1 parent 1c57e36 commit 4d034a7
Show file tree
Hide file tree
Showing 6 changed files with 1,777 additions and 1,618 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Min and max value for gr.Number by [@artegoser](https://github.com/artegoser) and [@dawoodkhan82] (https://github.com/dawoodkhan82) in [PR 3991](https://github.com/gradio-app/gradio/pull/3991)
- Support for asynchronous iterators by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3821](https://github.com/gradio-app/gradio/pull/3821)
- Returning language agnostic types in the `/info` route by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 4039](https://github.com/gradio-app/gradio/pull/4039)
- 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"
"""
import inspect
import os
import sys
from pathlib import Path

from uvicorn import Config
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(
"\nWARNING: 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

0 comments on commit 4d034a7

Please sign in to comment.