Skip to content

Commit

Permalink
Support custom components in gr.load (#8200)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored and dawoodkhan82 committed May 6, 2024
1 parent c5277a3 commit 6cac294
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 35 deletions.
25 changes: 25 additions & 0 deletions .changeset/fuzzy-mirrors-scream.md
Original file line number Diff line number Diff line change
@@ -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">

6 changes: 5 additions & 1 deletion client/python/gradio_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,11 @@ def process_input_files(self, *data) -> tuple:
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_)

Expand Down
68 changes: 41 additions & 27 deletions gradio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

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}")
import gradio.components as components_module
from gradio.components import Component

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):
Expand Down
12 changes: 5 additions & 7 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from gradio_client import Client

import gradio as gr
import gradio.utils


def pytest_configure(config):
Expand All @@ -21,22 +22,19 @@ 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,
gr.LoginButton,
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)

Expand Down
8 changes: 8 additions & 0 deletions test/test_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit 6cac294

Please sign in to comment.