diff --git a/.changeset/fuzzy-mirrors-scream.md b/.changeset/fuzzy-mirrors-scream.md new file mode 100644 index 0000000000000..62538b52ffa56 --- /dev/null +++ b/.changeset/fuzzy-mirrors-scream.md @@ -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() +``` + +image + diff --git a/client/python/gradio_client/client.py b/client/python/gradio_client/client.py index 2f9843565f8f6..d2905d6cb7f33 100644 --- a/client/python/gradio_client/client.py +++ b/client/python/gradio_client/client.py @@ -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_) diff --git a/gradio/utils.py b/gradio/utils.py index 22576fa740cdb..52b3960f003b7 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -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 - - 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): diff --git a/test/conftest.py b/test/conftest.py index 7832df08c765a..ccb17fe5a4feb 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,6 +6,7 @@ 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) diff --git a/test/test_external.py b/test/test_external.py index 307e2b7c45cd1..39b6b3c3d55a5 100644 --- a/test/test_external.py +++ b/test/test_external.py @@ -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)