diff --git a/.changeset/blue-chicken-sin.md b/.changeset/blue-chicken-sin.md new file mode 100644 index 00000000..1c7c8f55 --- /dev/null +++ b/.changeset/blue-chicken-sin.md @@ -0,0 +1,5 @@ +--- +'modelscope_studio': patch +--- + +feat: add assertions to the Application component diff --git a/backend/modelscope_studio/components/base/application/__init__.py b/backend/modelscope_studio/components/base/application/__init__.py index 47df9842..b6f8f8a0 100644 --- a/backend/modelscope_studio/components/base/application/__init__.py +++ b/backend/modelscope_studio/components/base/application/__init__.py @@ -5,7 +5,8 @@ from gradio.data_classes import GradioModel from gradio.events import EventListener -from ....utils.dev import ModelScopeDataLayoutComponent, resolve_frontend_dir +from ....utils.dev import (AppContext, ModelScopeDataLayoutComponent, + resolve_frontend_dir) class ApplicationPageScreenData(GradioModel): @@ -65,6 +66,7 @@ def __init__( key: int | str | None = None, render: bool = True, **kwargs): + AppContext.set_app(self) super().__init__(value=value, visible=visible, render=render, diff --git a/backend/modelscope_studio/components/legacy/Chatbot/__init__.py b/backend/modelscope_studio/components/legacy/Chatbot/__init__.py index ef0adc34..d70c54db 100644 --- a/backend/modelscope_studio/components/legacy/Chatbot/__init__.py +++ b/backend/modelscope_studio/components/legacy/Chatbot/__init__.py @@ -11,7 +11,7 @@ from gradio_client import utils as client_utils from gradio_client.documentation import document, set_documentation_group -from ....utils.dev import (CustomComponentDict, process_links, +from ....utils.dev import (AppContext, CustomComponentDict, process_links, resolve_frontend_dir) from ..MultimodalInput import MultimodalInputData @@ -147,6 +147,7 @@ def __init__( preview: If True (default), will enable image preview. custom_components: Define custom tags for markdown rendering. """ + AppContext.assert_app() self.likeable = likeable self.height = height self.rtl = rtl diff --git a/backend/modelscope_studio/components/legacy/Flow/__init__.py b/backend/modelscope_studio/components/legacy/Flow/__init__.py index 5de50c11..600d46d0 100644 --- a/backend/modelscope_studio/components/legacy/Flow/__init__.py +++ b/backend/modelscope_studio/components/legacy/Flow/__init__.py @@ -9,7 +9,7 @@ from gradio.events import Events from gradio_client.documentation import document -from ....utils.dev import CustomComponentDict, resolve_frontend_dir +from ....utils.dev import AppContext, CustomComponentDict, resolve_frontend_dir from .edge import * from .edge import Edge from .node import * @@ -88,6 +88,7 @@ def __init__( max_zoom: The maximum zoom level. custom_components: Define the custom node types for the flow schema. """ + AppContext.assert_app() self.height = height self.custom_components = custom_components self.show_sidebar = show_sidebar diff --git a/backend/modelscope_studio/components/legacy/Lifecycle/__init__.py b/backend/modelscope_studio/components/legacy/Lifecycle/__init__.py index cbcf1e30..33b0290d 100644 --- a/backend/modelscope_studio/components/legacy/Lifecycle/__init__.py +++ b/backend/modelscope_studio/components/legacy/Lifecycle/__init__.py @@ -7,7 +7,7 @@ from gradio.events import EventListener from gradio_client.documentation import document, set_documentation_group -from ....utils.dev import resolve_frontend_dir +from ....utils.dev import AppContext, resolve_frontend_dir set_documentation_group("component") @@ -67,6 +67,7 @@ def __init__( Parameters: every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. Queue must be enabled. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute. """ + AppContext.assert_app() self._bind_mount_event = _bind_mount_event self._bind_resize_event = _bind_resize_event self._bind_unmount_event = _bind_unmount_event diff --git a/backend/modelscope_studio/components/legacy/Markdown/__init__.py b/backend/modelscope_studio/components/legacy/Markdown/__init__.py index 19583b17..06efc53c 100644 --- a/backend/modelscope_studio/components/legacy/Markdown/__init__.py +++ b/backend/modelscope_studio/components/legacy/Markdown/__init__.py @@ -7,7 +7,7 @@ from gradio.events import Events from gradio_client.documentation import document, set_documentation_group -from ....utils.dev import (CustomComponentDict, process_links, +from ....utils.dev import (AppContext, CustomComponentDict, process_links, resolve_frontend_dir) set_documentation_group("component") @@ -78,6 +78,7 @@ def __init__( header_links: If True, will automatically create anchors for headings, displaying a link icon on hover. custom_components: Define custom tags for markdown rendering. """ + AppContext.assert_app() self.rtl = rtl self.enable_latex = enable_latex self.latex_single_dollar_delimiter = latex_single_dollar_delimiter diff --git a/backend/modelscope_studio/components/legacy/MultimodalInput/__init__.py b/backend/modelscope_studio/components/legacy/MultimodalInput/__init__.py index 33719a36..9bb3e237 100644 --- a/backend/modelscope_studio/components/legacy/MultimodalInput/__init__.py +++ b/backend/modelscope_studio/components/legacy/MultimodalInput/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path -from typing import Any, Callable, List, Literal, Optional, Union +from typing import Any, Callable, List, Literal, Union from gradio.components.base import FormComponent from gradio.data_classes import FileData, GradioModel @@ -9,7 +9,7 @@ from gradio_client import utils as client_utils from gradio_client.documentation import document, set_documentation_group -from ....utils.dev import resolve_frontend_dir +from ....utils.dev import AppContext, resolve_frontend_dir set_documentation_group("component") @@ -113,6 +113,7 @@ def __init__(self, file_preview_props: FilePreview will render if `value.files` is not empty, accepting the following props: height(int). webcam_props: Webcam will render if `sources` contains "webcam", accepting the following props: mirror_webcam(bool), include_audio(bool). """ + AppContext.assert_app() if type not in ["text", "password", "email"]: raise ValueError( '`type` must be one of "text", "password", or "email".') diff --git a/backend/modelscope_studio/components/legacy/WaterfallGallery/__init__.py b/backend/modelscope_studio/components/legacy/WaterfallGallery/__init__.py index 1b132b15..0b3dd8fe 100644 --- a/backend/modelscope_studio/components/legacy/WaterfallGallery/__init__.py +++ b/backend/modelscope_studio/components/legacy/WaterfallGallery/__init__.py @@ -14,7 +14,7 @@ from gradio_client.utils import is_http_url_like from PIL import Image as _Image # using _ to minimize namespace pollution -from ....utils.dev import resolve_frontend_dir +from ....utils.dev import AppContext, resolve_frontend_dir set_documentation_group("component") @@ -119,6 +119,7 @@ def __init__(self, likeable: Whether the gallery image display a like or dislike button. Set automatically by the .like method but has to be present in the signature for it to show up in the config. clickable: Whether the gallery image display an action button. Set automatically by the .click method but has to be present in the signature for it to show up in the config. """ + AppContext.assert_app() self.columns = columns self.height = height self.gap = gap diff --git a/backend/modelscope_studio/utils/dev/__init__.py b/backend/modelscope_studio/utils/dev/__init__.py index 393cd789..0f4c48ee 100644 --- a/backend/modelscope_studio/utils/dev/__init__.py +++ b/backend/modelscope_studio/utils/dev/__init__.py @@ -1,5 +1,6 @@ from typing import List, Optional, TypedDict +from .app_context import * from .component import * from .process_links import process_links from .resolve_frontend_dir import * diff --git a/backend/modelscope_studio/utils/dev/app_context.py b/backend/modelscope_studio/utils/dev/app_context.py new file mode 100644 index 00000000..1301e39b --- /dev/null +++ b/backend/modelscope_studio/utils/dev/app_context.py @@ -0,0 +1,21 @@ +class AppContext: + _app = None + + @classmethod + def set_app(cls, app): + cls._app = app + + @classmethod + def has_app(cls): + return cls._app is not None + + @classmethod + def assert_app(cls): + if cls._app is None: + raise ImportError( + """: Cannot find the `Application` component, did you forget to import it from `modelscope_studio.components.base`?""" + ) + + @classmethod + def get_app(cls): + return cls._app diff --git a/backend/modelscope_studio/utils/dev/component.py b/backend/modelscope_studio/utils/dev/component.py index 087ee443..f271dad6 100644 --- a/backend/modelscope_studio/utils/dev/component.py +++ b/backend/modelscope_studio/utils/dev/component.py @@ -4,6 +4,8 @@ from gradio.component_meta import ComponentMeta from gradio.components.base import BlockContext, Component +from .app_context import AppContext + class ModelScopeLayoutComponent(BlockContext, metaclass=ComponentMeta): """ @@ -32,6 +34,7 @@ def __init__( elem_id=elem_id, elem_classes=elem_classes, render=render) + AppContext.assert_app() self.as_item = as_item if self.parent: self._internal = dict(index=len(self.parent.children) - 1) @@ -75,6 +78,8 @@ def __init__( inputs=inputs, load_fn=load_fn, render=render) + AppContext.assert_app() + if self.parent: self._internal = dict(index=len(self.parent.children) - 1) else: @@ -142,6 +147,8 @@ def __init__( elem_id=elem_id, elem_classes=elem_classes, render=render) + AppContext.assert_app() + if self.parent: self._internal = dict(index=len(self.parent.children) - 1) else: