Support custom components in gr.load (#8200)

* Add code

* add changeset

* Update fuzzy-mirrors-scream.md

* Update fuzzy-mirrors-scream.md

* Fix tests

* Update .changeset/fuzzy-mirrors-scream.md

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Fix code

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
Freddy Boulton 2024-05-02 19:07:45 -04:00 committed by GitHub
parent cf52ca6a51
commit 72039be93a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 34 deletions

View File

@ -0,0 +1,25 @@
---
"gradio": patch
"gradio_client": patch
---
highlight:
#### Support custom components in gr.load
It is now possible to load a demo with a custom component with `gr.load`.
The custom component must be installed in your system and imported in your python session.
```python
import gradio as gr
import gradio_pdf
demo = gr.load("freddyaboulton/gradiopdf", src="spaces")
if __name__ == "__main__":
demo.launch()
```
<img width="1284" alt="image" src="https://github.com/gradio-app/gradio/assets/41651716/9c3e846b-f3f2-4c1c-8cb6-53a6d186aaa0">

View File

@ -1171,7 +1171,11 @@ class Endpoint:
or utils.is_http_url_like(f),
)
elif not self.client.upload_files:
d = utils.traverse(d, self._upload_file, utils.is_file_obj_with_meta)
d = utils.traverse(
d,
partial(self._upload_file, data_index=i),
utils.is_file_obj_with_meta,
)
data_.append(d)
return tuple(data_)

View File

@ -518,6 +518,30 @@ def resolve_singleton(_list: list[Any] | Any) -> Any:
return _list
def get_all_components() -> list[type[Component] | type[BlockContext]]:
import gradio as gr
classes_to_check = (
gr.components.Component.__subclasses__()
+ gr.blocks.BlockContext.__subclasses__() # type: ignore
)
subclasses = []
while classes_to_check:
subclass = classes_to_check.pop()
classes_to_check.extend(subclass.__subclasses__())
subclasses.append(subclass)
return subclasses
def core_gradio_components():
return [
class_
for class_ in get_all_components()
if class_.__module__.startswith("gradio.")
]
def component_or_layout_class(cls_name: str) -> type[Component] | type[BlockContext]:
"""
Returns the component, template, or layout class with the given class name, or
@ -528,33 +552,23 @@ def component_or_layout_class(cls_name: str) -> type[Component] | type[BlockCont
Returns:
cls: the component class
"""
import gradio.blocks
import gradio.components
import gradio.layouts
import gradio.templates
import gradio.components as components_module
from gradio.components import Component
components = [
(name, cls)
for name, cls in gradio.components.__dict__.items()
if isinstance(cls, type)
]
templates = [
(name, cls)
for name, cls in gradio.templates.__dict__.items()
if isinstance(cls, type)
]
layouts = [
(name, cls)
for name, cls in gradio.layouts.__dict__.items()
if isinstance(cls, type)
]
for name, cls in components + templates + layouts:
if name.lower() == cls_name.replace("_", "") and (
issubclass(cls, gradio.components.Component)
or issubclass(cls, gradio.blocks.BlockContext)
):
return cls
raise ValueError(f"No such component or layout: {cls_name}")
components = {c.__name__.lower(): c for c in get_all_components()}
# add aliases such as 'text'
for name, cls in components_module.__dict__.items():
if isinstance(cls, type) and issubclass(cls, Component):
components[name.lower()] = cls
if cls_name.replace("_", "") in components:
return components[cls_name.replace("_", "")]
raise ValueError(
f"No such component or layout: {cls_name}. "
"It is possible it is a custom component, "
"in which case make sure it is installed and imported in your python session."
)
def run_coro_in_background(func: Callable, *args, **kwargs):

View File

@ -6,6 +6,7 @@ import pytest
from gradio_client import Client
import gradio as gr
import gradio.utils
def pytest_configure(config):
@ -21,11 +22,9 @@ def test_file_dir():
@pytest.fixture
def io_components():
classes_to_check = gr.components.Component.__subclasses__()
all_subclasses = gradio.utils.core_gradio_components()
subclasses = []
while classes_to_check:
subclass = classes_to_check.pop()
for subclass in all_subclasses:
if subclass in [
gr.components.FormComponent,
gr.State,
@ -33,10 +32,9 @@ def io_components():
gr.LogoutButton,
]:
continue
children = subclass.__subclasses__()
if subclass in gr.components.FormComponent.__subclasses__():
continue
if children:
classes_to_check.extend(children)
if "value" in inspect.signature(subclass.__init__).parameters:
subclasses.append(subclass)

View File

@ -514,3 +514,11 @@ def test_use_api_name_in_call_method():
# app = gr.load(name="spaces/gradio/multiple-api-name-test")
# assert app(15, api_name="minus_one") == 14
# assert app(4, api_name="double") == 8
def test_load_custom_component():
demo = gr.load("spaces/freddyaboulton/gradiopdf")
output = demo(
"test/test_files/sample_file.pdf", "What does this say?", api_name="predict"
)
assert isinstance(output, str)