Skip to content

Commit e5d9c34

Browse files
committed
Framework/Gradio: Add basic example about Gradio, reading sys.summits
1 parent 891a656 commit e5d9c34

File tree

9 files changed

+290
-0
lines changed

9 files changed

+290
-0
lines changed

.github/dependabot.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ updates:
8080
schedule:
8181
interval: "daily"
8282

83+
- directory: "/framework/gradio"
84+
package-ecosystem: "pip"
85+
schedule:
86+
interval: "daily"
87+
8388
- directory: "/framework/streamlit"
8489
package-ecosystem: "pip"
8590
schedule:
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Gradio
2+
3+
on:
4+
pull_request:
5+
branches: ~
6+
paths:
7+
- '.github/workflows/framework-gradio.yml'
8+
- 'framework/gradio/**'
9+
- '/requirements.txt'
10+
push:
11+
branches: [ main ]
12+
paths:
13+
- '.github/workflows/framework-gradio.yml'
14+
- 'framework/gradio/**'
15+
- '/requirements.txt'
16+
17+
# Allow job to be triggered manually.
18+
workflow_dispatch:
19+
20+
# Run job each night after CrateDB nightly has been published.
21+
schedule:
22+
- cron: '0 3 * * *'
23+
24+
# Cancel in-progress jobs when pushing to the same branch.
25+
concurrency:
26+
cancel-in-progress: true
27+
group: ${{ github.workflow }}-${{ github.ref }}
28+
29+
jobs:
30+
test:
31+
name: "
32+
Python: ${{ matrix.python-version }}
33+
CrateDB: ${{ matrix.cratedb-version }}
34+
on ${{ matrix.os }}"
35+
runs-on: ${{ matrix.os }}
36+
strategy:
37+
fail-fast: false
38+
matrix:
39+
os: [ 'ubuntu-latest' ]
40+
python-version: [ '3.10', '3.11', '3.12' ]
41+
cratedb-version: [ 'nightly' ]
42+
43+
services:
44+
cratedb:
45+
image: crate/crate:${{ matrix.cratedb-version }}
46+
ports:
47+
- 4200:4200
48+
- 5432:5432
49+
env:
50+
CRATE_HEAP_SIZE: 4g
51+
52+
steps:
53+
54+
- name: Acquire sources
55+
uses: actions/checkout@v4
56+
57+
- name: Set up Python
58+
uses: actions/setup-python@v5
59+
with:
60+
python-version: ${{ matrix.python-version }}
61+
architecture: x64
62+
cache: 'pip'
63+
cache-dependency-path: |
64+
requirements.txt
65+
framework/gradio/requirements.txt
66+
framework/gradio/requirements-dev.txt
67+
68+
- name: Install utilities
69+
run: |
70+
pip install -r requirements.txt
71+
72+
- name: Validate framework/gradio
73+
run: |
74+
ngr test --accept-no-venv framework/gradio

framework/gradio/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/flagged

framework/gradio/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Gradio with CrateDB Example
2+
3+
## About
4+
Demonstrate connectivity from Gradio to CrateDB.
5+
6+
## Configuration
7+
Configure database connection address and credentials by using
8+
an SQLAlchemy connection string, see variable `CRATEDB_SQLALCHEMY_URL`
9+
in `basic_sys_summits.py`. Please make sure to use valid credentials
10+
matching your environment.
11+
12+
## Usage
13+
14+
### CrateDB on localhost
15+
To start a CrateDB instance on your machine, invoke:
16+
```shell
17+
docker run -it --rm \
18+
--publish=4200:4200 --publish=5432:5432 \
19+
--env=CRATE_HEAP_SIZE=2g \
20+
crate:latest -Cdiscovery.type=single-node
21+
```
22+
23+
### CrateDB Cloud
24+
Please have a look at the [basic_sys_summits.py](basic_sys_summits.py) program
25+
as a blueprint. It includes a `CRATEDB_SQLALCHEMY_URL` variable definition
26+
that configures the application to connect to CrateDB Cloud.
27+
```python
28+
CRATEDB_SQLALCHEMY_URL = "crate://admin:g_,8.F0fNbVSk0.*!n54S5c,@example.gke1.us-central1.gcp.cratedb.net:4200?ssl=true"```
29+
```
30+
31+
Install dependencies.
32+
```shell
33+
pip install -r requirements.txt
34+
```
35+
36+
Invoke Gradio to serve the application.
37+
```shell
38+
gradio basic_sys_summits.py
39+
```
40+
41+
## Screenshot
42+
43+
Enjoy the list of mountains.
44+
45+
![image](https://github.com/crate/cratedb-examples/assets/453543/af417966-b694-45ec-9391-f0e99a2ac014)
46+
47+
48+
## Development
49+
50+
Acquire `cratedb-example` repository, and set up development sandbox.
51+
```shell
52+
git clone https://github.com/crate/cratedb-examples
53+
cd cratedb-examples
54+
python3 -m venv .venv
55+
source .venv/bin/activate
56+
pip install -r requirements.txt
57+
```
58+
59+
Invoke the integration test cases.
60+
```shell
61+
ngr test framework/gradio
62+
```

framework/gradio/basic_sys_summits.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
A basic Gradio application connecting to CrateDB using SQLAlchemy.
3+
4+
It reads the built-in `sys.summits` table into a dataframe, and
5+
displays its contents.
6+
7+
- https://www.gradio.app/guides/connecting-to-a-database
8+
- https://www.gradio.app/docs/gradio/dataframe
9+
- https://cratedb.com/docs/sqlalchemy-cratedb/
10+
"""
11+
import gradio as gr
12+
import pandas as pd
13+
14+
# Connect to CrateDB on localhost.
15+
CRATEDB_SQLALCHEMY_URL = "crate://localhost:4200"
16+
17+
# Connect to CrateDB Cloud.
18+
# CRATEDB_SQLALCHEMY_URL = "crate://admin:g_,8.F0fNbVSk0.*!n54S5c,@example.gke1.us-central1.gcp.cratedb.net:4200?ssl=true"
19+
20+
21+
def get_sys_summits():
22+
"""
23+
Query the database using pandas.
24+
"""
25+
return pd.read_sql('SELECT * FROM "sys"."summits";', con=CRATEDB_SQLALCHEMY_URL)
26+
27+
28+
def get_dataframe():
29+
"""
30+
Create a dataframe widget.
31+
"""
32+
df = get_sys_summits()
33+
return gr.DataFrame(df)
34+
35+
36+
# Define the Gradio interface.
37+
demo = gr.Interface(
38+
fn=get_dataframe,
39+
inputs=[],
40+
outputs=["dataframe"],
41+
title="Gradio with CrateDB Example",
42+
live=True,
43+
allow_flagging="never",
44+
)
45+
46+
47+
if __name__ == "__main__":
48+
demo.launch()

framework/gradio/pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[tool.pytest.ini_options]
2+
minversion = "2.0"
3+
addopts = """
4+
-rfEX -p pytester --strict-markers --verbosity=3
5+
--capture=no
6+
"""
7+
log_level = "DEBUG"
8+
log_cli_level = "DEBUG"
9+
testpaths = ["*.py"]
10+
xfail_strict = true
11+
markers = [
12+
]

framework/gradio/requirements-dev.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gradio-client==1.0.*
2+
pytest<9

framework/gradio/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gradio==4.*
2+
sqlalchemy-cratedb==0.38.0

framework/gradio/test.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import logging
2+
import shlex
3+
import subprocess
4+
import sys
5+
import threading
6+
import time
7+
from urllib.error import HTTPError
8+
from urllib.request import urlopen
9+
10+
import pytest
11+
from gradio_client import Client
12+
13+
14+
logger = logging.getLogger()
15+
16+
GRADIO_SERVER_PORT = "7861"
17+
GRADIO_API_URL = f"http://localhost:{GRADIO_SERVER_PORT}"
18+
19+
20+
def run(command: str, env=None, timeout: int = None):
21+
"""
22+
Invoke a command in a subprocess.
23+
"""
24+
env = env or {}
25+
env["PATH"] = ""
26+
subprocess.call(shlex.split(command), env=env, timeout=timeout)
27+
28+
29+
def check_url(url):
30+
"""
31+
Check if a service is reachable.
32+
33+
Makes a simple GET request to path of the HTTP endpoint. Service is
34+
available if returned status code is < 500.
35+
"""
36+
try:
37+
r = urlopen(url)
38+
return r.code < 500
39+
except HTTPError as e:
40+
# If service returns e.g. a 404 it's ok
41+
return e.code < 500
42+
except Exception:
43+
# Possible service not yet started
44+
return False
45+
46+
47+
@pytest.fixture(scope="session", autouse=True)
48+
def run_server():
49+
50+
def server_thread():
51+
print("sys.exec:", sys.executable)
52+
try:
53+
run(f"{sys.executable} basic_sys_summits.py", env={"GRADIO_SERVER_PORT": GRADIO_SERVER_PORT}, timeout=5)
54+
except subprocess.TimeoutExpired:
55+
pass
56+
57+
thread = threading.Thread(target=server_thread)
58+
try:
59+
thread.start()
60+
except KeyboardInterrupt:
61+
pass
62+
63+
while not check_url(GRADIO_API_URL):
64+
logger.info("Waiting for Gradio API to come up")
65+
time.sleep(0.2)
66+
67+
68+
def test_read_sys_summits():
69+
"""
70+
Verify reading CrateDB's built-in `sys.summits` database table through the Gradio API.
71+
72+
- https://www.gradio.app/guides/getting-started-with-the-python-client
73+
- https://www.gradio.app/docs/python-client/
74+
- https://www.gradio.app/docs/gradio/dataframe
75+
"""
76+
77+
# Connect to Gradio API, and submit a request.
78+
client = Client(GRADIO_API_URL)
79+
result = client.predict(api_name="/predict")
80+
81+
# Verify result.
82+
assert "mountain" in result["value"]["headers"]
83+
assert len(result["value"]["data"]) > 80
84+
assert result["value"]["data"][0][5] == "Mont Blanc"

0 commit comments

Comments
 (0)