|
|
|
# `gradio_logsview` |
|
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.12%20-%20orange"> |
|
|
|
Visualize logs in your Gradio app |
|
|
|
## Installation |
|
|
|
```bash |
|
pip install gradio_logsview |
|
``` |
|
|
|
## Usage |
|
|
|
```python |
|
import logging |
|
import random |
|
import time |
|
|
|
import gradio as gr |
|
from gradio_logsview import LogsView, LogsViewRunner |
|
from tqdm import tqdm |
|
|
|
logger = logging.getLogger("custom.foo") |
|
|
|
def random_values(failing: bool = False): |
|
for i in tqdm(range(10)): |
|
logger.log( |
|
random.choice( |
|
[ # Random levels |
|
logging.INFO, |
|
logging.DEBUG, |
|
logging.WARNING, |
|
logging.ERROR, |
|
logging.CRITICAL, |
|
] |
|
), |
|
f"Value {i+1}", # Random values |
|
) |
|
time.sleep(random.uniform(0, 1)) |
|
if failing and i == 5: |
|
raise ValueError("Failing!!") |
|
|
|
|
|
def fn_command_success(): |
|
runner = LogsViewRunner() |
|
yield from runner.run_command(["python", "-u", "demo/script.py"]) |
|
yield runner.log(f"Runner: {runner}") |
|
|
|
|
|
def fn_command_failing(): |
|
runner = LogsViewRunner() |
|
yield from runner.run_command(["python", "-u", "demo/script.py", "--failing"]) |
|
yield runner.log(f"Runner: {runner}") |
|
|
|
|
|
def fn_python_success(): |
|
runner = LogsViewRunner() |
|
yield from runner.run_python(random_values, log_level=logging.INFO, logger_name="custom.foo", failing=False) |
|
yield runner.log(f"Runner: {runner}") |
|
|
|
|
|
def fn_python_failing(): |
|
runner = LogsViewRunner() |
|
yield from runner.run_python(random_values, log_level=logging.INFO, logger_name="custom.foo", failing=True) |
|
yield runner.log(f"Runner: {runner}") |
|
|
|
|
|
markdown_top = """ |
|
# LogsView Demo |
|
|
|
This demo shows how to use the `LogsView` component to run some Python code or execute a command and display logs in real-time. |
|
|
|
Click on any button to launch a process and see the logs displayed in real-time. |
|
In the Python examples, code is executed in a process. You can see the logs (generated randomly with different log levels). |
|
In the command examples, the command line is executed on the system directly. Any command can be executed. |
|
""" |
|
|
|
|
|
markdown_bottom = """ |
|
## Installation |
|
|
|
``` |
|
pip install https://huggingface.co/spaces/Wauplin/gradio_logsview/resolve/main/gradio_logsview-0.0.5-py3-none-any.whl |
|
``` |
|
|
|
or add this line to your `requirements.txt`: |
|
|
|
``` |
|
gradio_logsview@https://huggingface.co/spaces/Wauplin/gradio_logsview/resolve/main/gradio_logsview-0.0.5-py3-none-any.whl |
|
``` |
|
|
|
## How to run Python code? |
|
|
|
With `LogsView.run_python`, you can run Python code in a separate process and capture logs in real-time. |
|
You can configure which logs to capture (log level and logger name). |
|
|
|
```py |
|
from gradio_logsview import LogsView |
|
|
|
def fn_python(): |
|
# Run `my_function` in a separate process |
|
# All logs above `INFO` level will be captured and displayed in real-time. |
|
runner = LogsViewRunner() # Initialize the runner |
|
yield from runner.run_python(my_function, log_level=logging.INFO, arg1="value1") |
|
yield runner.log(f"Runner: {runner}") # Log any message |
|
if runner.exit_code != 0: |
|
# Handle error |
|
... |
|
|
|
with gr.Blocks() as demo: |
|
logs = LogsView() |
|
btn = gr.Button("Run Python code") |
|
btn.click(fn_python, outputs=logs) |
|
``` |
|
|
|
## How to run a command? |
|
|
|
With `LogsView.run_command`, you can run a command on the system and capture logs from the process in real-time. |
|
|
|
```py |
|
from gradio_logsview import LogsView |
|
|
|
def fn_command(): |
|
# Run a command and capture all logs from the subprocess |
|
runner = LogsViewRunner() # Initialize the runner |
|
yield from runner.run_command( |
|
cmd=["mergekit-yaml", "config.yaml", "merge", "--copy-", "--cuda", "--low-cpu-memory"] |
|
) |
|
yield runner.log(f"Runner: {runner}") # Log any message |
|
if runner.exit_code != 0: |
|
# Handle error |
|
... |
|
|
|
with gr.Blocks() as demo: |
|
logs = LogsView() |
|
btn = gr.Button("Run command") |
|
btn.click(fn_command, outputs=logs) |
|
``` |
|
|
|
## TODO |
|
|
|
- [ ] display logs with colors (front-end) |
|
- [ ] format logs client-side (front-end) |
|
- [ ] scrollable logs if more than N lines (front-end) |
|
- [ ] format each log only once (front-end) |
|
- [x] stop process if `run_command` gets cancelled (back-end) |
|
- [x] correctly pass error stacktrace in `run_python` (back-end) |
|
- [x] correctly catch print statements in `run_python` (back-end) |
|
- [ ] disable interactivity + remove all code editing logic (both?) |
|
- [x] how to handle progress bars? (i.e when logs are overwritten in terminal) |
|
- [ ] Have 3 tabs in UI for stdout, stderr and logging (front-end + back-end) |
|
- [ ] Write logger name in the logs (back-end) |
|
""" |
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown(markdown_top) |
|
|
|
with gr.Row(): |
|
btn_python_success = gr.Button("Run Python code (success)") |
|
btn_python_failing = gr.Button("Run Python code (failing)") |
|
with gr.Row(): |
|
btn_command_success = gr.Button("Run command (success)") |
|
btn_command_failing = gr.Button("Run command (failing)") |
|
logs = LogsView() |
|
|
|
gr.Markdown(markdown_bottom) |
|
|
|
btn_python_failing.click(fn_python_failing, outputs=logs) |
|
btn_python_success.click(fn_python_success, outputs=logs) |
|
btn_command_failing.click(fn_command_failing, outputs=logs) |
|
btn_command_success.click(fn_command_success, outputs=logs) |
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch() |
|
|
|
``` |
|
|
|
## `Log` |
|
|
|
### Initialization |
|
|
|
|
|
|
|
|
|
|
|
## `LogsView` |
|
|
|
### Initialization |
|
|
|
|
|
|
|
|
|
### User function |
|
|
|
The impact on the users predict function varies depending on whether the component is used as an input or output for an event (or both). |
|
|
|
- When used as an Input, the component only impacts the input signature of the user function. |
|
- When used as an output, the component only impacts the return signature of the user function. |
|
|
|
The code snippet below is accurate in cases where the component is used as both an input and an output. |
|
|
|
- **As output:** Is passed, passes the code entered as a `str`. |
|
- **As input:** Should return, expects a list of `Log` logs. |
|
|
|
```python |
|
def predict( |
|
value: NoReturn |
|
) -> list[Log]: |
|
return value |
|
``` |
|
|
|
|
|
## `LogsViewRunner` |
|
|
|
### Initialization |
|
|
|
|
|
|
|
|
|
|