Skip to content

Commit d348f26

Browse files
committed
add gradio app mode support
1 parent f2aae49 commit d348f26

File tree

8 files changed

+130
-18
lines changed

8 files changed

+130
-18
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## Unreleased
88

9+
### Added
10+
11+
- You can now deploy Gradio applications. This requires Posit Connect release 2024.11.0
12+
or later. Use `rsconnect deploy gradio` to deploy, or `rsconnect write-manifest gradio`
13+
to create a manifest file.
14+
915
### Changed
1016

1117
- The `rsconnect content build run --poll-wait` argument specifies an integral

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ This package provides a CLI (command-line interface) for interacting
44
with and deploying to Posit Connect. This is also used by the
55
[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy
66
Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit
7-
Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and
8-
Bokeh applications.
7+
Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit,
8+
Gradio, and Bokeh applications.
99

1010
Content types not directly supported by the CLI may also be deployed if they include a
1111
prepared `manifest.json` file. See ["Deploying R or Other
@@ -288,9 +288,10 @@ You can deploy a variety of APIs and applications using sub-commands of the
288288
* `dash`: Python Dash apps
289289
* `streamlit`: Streamlit apps
290290
* `bokeh`: Bokeh server apps
291+
* `gradio`: Gradio apps
291292

292293
All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`,
293-
and `bokeh` sub-commands.
294+
`gradio`, and `bokeh` sub-commands.
294295

295296
#### Including Extra Files
296297

@@ -470,8 +471,8 @@ rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb
470471
```
471472

472473
When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`,
473-
`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory
474-
containing the API or application.
474+
`rsconnect deploy streamlit`, `rsconnect deploy bokeh`, or `rsconnect deploy gradio`,
475+
the title is derived from the directory containing the API or application.
475476

476477
When using `rsconnect deploy manifest`, the title is derived from the primary
477478
filename referenced in the manifest.
@@ -726,7 +727,7 @@ rsconnect content search --help
726727
# -c, --cacert FILENAME The path to trusted TLS CA certificates.
727728
# --published Search only published content.
728729
# --unpublished Search only unpublished content.
729-
# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static]
730+
# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|python-gradio|quarto-shiny|quarto-static]
730731
# Filter content results by content type.
731732
# --r-version VERSIONSEARCHFILTER
732733
# Filter content results by R version.

rsconnect/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1738,7 +1738,7 @@ def deploy_app(
17381738
generate_deploy_python(app_mode=AppModes.STREAMLIT_APP, alias="streamlit", min_version="1.8.4")
17391739
generate_deploy_python(app_mode=AppModes.BOKEH_APP, alias="bokeh", min_version="1.8.4")
17401740
generate_deploy_python(app_mode=AppModes.PYTHON_SHINY, alias="shiny", min_version="2022.07.0")
1741-
1741+
generate_deploy_python(app_mode=AppModes.PYTHON_GRADIO, alias="gradio", min_version="2024.11.0")
17421742

17431743
@deploy.command(
17441744
name="other-content",
@@ -2272,6 +2272,7 @@ def manifest_writer(
22722272
generate_write_manifest_python(AppModes.PYTHON_FASTAPI, alias="fastapi")
22732273
generate_write_manifest_python(AppModes.PYTHON_SHINY, alias="shiny")
22742274
generate_write_manifest_python(AppModes.STREAMLIT_APP, alias="streamlit")
2275+
generate_write_manifest_python(AppModes.PYTHON_GRADIO, alias="gradio")
22752276

22762277

22772278
# noinspection SpellCheckingInspection

rsconnect/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class AppModes:
9797
STATIC_QUARTO = AppMode(14, "quarto-static", "Quarto Document", ".qmd")
9898
PYTHON_SHINY = AppMode(15, "python-shiny", "Python Shiny Application")
9999
JUPYTER_VOILA = AppMode(16, "jupyter-voila", "Jupyter Voila Application")
100+
PYTHON_GRADIO = AppMode(17, "python-gradio", "Gradio Application")
100101

101102
_modes = [
102103
UNKNOWN,
@@ -116,6 +117,7 @@ class AppModes:
116117
STATIC_QUARTO,
117118
PYTHON_SHINY,
118119
JUPYTER_VOILA,
120+
PYTHON_GRADIO
119121
]
120122

121123
Modes = Literal[
@@ -136,6 +138,7 @@ class AppModes:
136138
"quarto-static",
137139
"python-shiny",
138140
"jupyter-voila",
141+
"python-gradio"
139142
]
140143

141144
_cloud_to_connect_modes = {

tests/test_bundle.py

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,9 +1059,7 @@ def test_make_tensorflow_manifest_empty(self):
10591059
manifest,
10601060
{
10611061
"version": 1,
1062-
"metadata": {
1063-
"appmode": "tensorflow-saved-model"
1064-
},
1062+
"metadata": {"appmode": "tensorflow-saved-model"},
10651063
"files": {},
10661064
},
10671065
)
@@ -1082,9 +1080,7 @@ def test_make_tensorflow_manifest(self):
10821080
manifest,
10831081
{
10841082
"version": 1,
1085-
"metadata": {
1086-
"appmode": "tensorflow-saved-model"
1087-
},
1083+
"metadata": {"appmode": "tensorflow-saved-model"},
10881084
"files": {
10891085
"1/saved_model.pb": {"checksum": mock.ANY},
10901086
},
@@ -1105,17 +1101,15 @@ def test_make_tensorflow_bundle(self):
11051101
[
11061102
"1/saved_model.pb",
11071103
"manifest.json",
1108-
],
1109-
)
1104+
],
1105+
)
11101106
manifest_data = tar.extractfile("manifest.json").read().decode("utf-8")
11111107
manifest = json.loads(manifest_data)
11121108
self.assertEqual(
11131109
manifest,
11141110
{
11151111
"version": 1,
1116-
"metadata": {
1117-
"appmode": "tensorflow-saved-model"
1118-
},
1112+
"metadata": {"appmode": "tensorflow-saved-model"},
11191113
"files": {
11201114
"1/saved_model.pb": {"checksum": mock.ANY},
11211115
},
@@ -2909,6 +2903,76 @@ def test_make_manifest_bundle():
29092903
assert manifest["files"].keys() == bundle_json["files"].keys()
29102904

29112905

2906+
gradio_dir = os.path.join(cur_dir, "./testdata/gradio")
2907+
gradio_file = os.path.join(cur_dir, "./testdata/gradio/app.py")
2908+
2909+
2910+
def test_make_api_manifest_gradio():
2911+
gradio_dir_ans = {
2912+
"version": 1,
2913+
"locale": "en_US.UTF-8",
2914+
"metadata": {"appmode": "python-gradio"},
2915+
"python": {
2916+
"version": "3.8.12",
2917+
"package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"},
2918+
},
2919+
"files": {
2920+
"requirements.txt": {"checksum": "381ccadfb8d4848add470e33033b198f"},
2921+
"app.py": {"checksum": "22feec76e9c02ac6b5a34a083e2983b6"},
2922+
},
2923+
}
2924+
environment = create_python_environment(
2925+
gradio_dir,
2926+
)
2927+
manifest, _ = make_api_manifest(
2928+
gradio_dir,
2929+
None,
2930+
AppModes.PYTHON_GRADIO,
2931+
environment,
2932+
None,
2933+
None,
2934+
)
2935+
2936+
assert gradio_dir_ans["metadata"] == manifest["metadata"]
2937+
assert gradio_dir_ans["files"].keys() == manifest["files"].keys()
2938+
2939+
2940+
def test_make_api_bundle_gradio():
2941+
gradio_dir_ans = {
2942+
"version": 1,
2943+
"locale": "en_US.UTF-8",
2944+
"metadata": {"appmode": "python-gradio"},
2945+
"python": {
2946+
"version": "3.8.12",
2947+
"package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"},
2948+
},
2949+
"files": {
2950+
"requirements.txt": {"checksum": "381ccadfb8d4848add470e33033b198f"},
2951+
"app.py": {"checksum": "22feec76e9c02ac6b5a34a083e2983b6"},
2952+
},
2953+
}
2954+
environment = create_python_environment(
2955+
gradio_dir,
2956+
)
2957+
with make_api_bundle(
2958+
gradio_dir,
2959+
None,
2960+
AppModes.PYTHON_GRADIO,
2961+
environment,
2962+
None,
2963+
None,
2964+
) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar:
2965+
names = sorted(tar.getnames())
2966+
assert names == [
2967+
"app.py",
2968+
"manifest.json",
2969+
"requirements.txt",
2970+
]
2971+
bundle_json = json.loads(tar.extractfile("manifest.json").read().decode("utf-8"))
2972+
assert gradio_dir_ans["metadata"] == bundle_json["metadata"]
2973+
assert gradio_dir_ans["files"].keys() == bundle_json["files"].keys()
2974+
2975+
29122976
empty_manifest_file = os.path.join(cur_dir, "./testdata/Manifest_data/empty_manifest.json")
29132977
missing_file_manifest = os.path.join(cur_dir, "./testdata/Manifest_data/missing_file_manifest.json")
29142978

tests/testdata/gradio/app.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import gradio as gr
2+
3+
def greet(name, intensity):
4+
return "Hello, " + name + "!" * int(intensity)
5+
6+
demo = gr.Interface(
7+
fn=greet,
8+
inputs=["text", "slider"],
9+
outputs=["text"],
10+
)
11+
12+
demo.launch(auth = ("username", "password"))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"version": 1,
3+
"locale": "en_US.UTF-8",
4+
"metadata": {
5+
"appmode": "python-gradio",
6+
"entrypoint": "app"
7+
},
8+
"python": {
9+
"version": "3.10.12",
10+
"package_manager": {
11+
"name": "pip",
12+
"version": "24.2",
13+
"package_file": "requirements.txt"
14+
}
15+
},
16+
"files": {
17+
"requirements.txt": {
18+
"checksum": "381ccadfb8d4848add470e33033b198f"
19+
},
20+
"app.py": {
21+
"checksum": "22feec76e9c02ac6b5a34a083e2983b6"
22+
}
23+
}
24+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gradio

0 commit comments

Comments
 (0)