From 5b484794f6c50aeb1d474069fd7798b3ad0bce84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fred=20Lef=C3=A9v=C3=A8re-Laoide?= <90181748+FredLL-Avaiga@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:41:07 +0100 Subject: [PATCH] support glob declaration in resource descriptions (#2192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * support glob declaration in resource descriptions * use Path.glob to be 3.9 compatible --------- Co-authored-by: Fred Lefévère-Laoide --- taipy/gui/extension/library.py | 28 ++++++++++++++++++++++------ taipy/gui/gui.py | 15 +++++---------- taipy/gui_core/_GuiCoreLib.py | 6 +++--- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/taipy/gui/extension/library.py b/taipy/gui/extension/library.py index 27c16021c3..4b0dc9bd88 100644 --- a/taipy/gui/extension/library.py +++ b/taipy/gui/extension/library.py @@ -16,7 +16,7 @@ from abc import ABC, abstractmethod from inspect import isclass from pathlib import Path -from urllib.parse import urlencode +from urllib.parse import urlencode, urlparse from .._renderers.builder import _Builder from .._warnings import _warn @@ -115,7 +115,7 @@ def __init__( default_property (str): The name of the default property for this element. properties (Dict[str, ElementProperty]): The dictionary containing the properties of this element, where the keys are the property names and the values are instances of ElementProperty. inner_properties (Optional[List[ElementProperty]]): The optional list of inner properties for this element.
- Default values are set/binded automatically. + Default values are set/bound automatically. react_component (Optional[str]): The name of the component to be created on the front-end.
If not specified, it is set to a camel case version of the element's name ("one_name" is transformed to "OneName"). @@ -324,9 +324,27 @@ def get_js_module_name(self) -> str: """ return _to_camel_case(self.get_name(), True) + def __get_class_folder(self): + if not hasattr(self, "_class_folder"): + module_obj = sys.modules.get(self.__class__.__module__) + base = (Path(".") if module_obj is None else Path(module_obj.__file__).parent).resolve() # type: ignore + self._class_folder = base if base.exists() else Path(".").resolve() + return self._class_folder + + def _do_get_relative_paths(self, paths: t.List[str]) -> t.List[str]: + ret = set() + for path in paths or []: + if bool(urlparse(path).netloc): + ret.add(path) + elif file_paths := self.__get_class_folder().glob(path): + ret.update([file_path.relative_to(self.__get_class_folder()).as_posix() for file_path in file_paths]) + elif path: + ret.add(path) + return list(ret) + def get_scripts(self) -> t.List[str]: """ - Return the list of the mandatory script file pathnames. + Return the list of the mandatory script file path names. If a script file pathname is an absolute URL it will be used as is.
If it's not it will be passed to `(ElementLibrary.)get_resource()^` to retrieve a local @@ -363,9 +381,7 @@ def get_resource(self, name: str) -> Path: Arguments: name (str): The name of the resource for which a local Path should be returned. """ # noqa: E501 - module_obj = sys.modules.get(self.__class__.__module__) - base = (Path(".") if module_obj is None else Path(module_obj.__file__).parent).resolve() # type: ignore - base = base if base.exists() else Path(".").resolve() + base = self.__get_class_folder() file = (base / name).resolve() if str(file).startswith(str(base)) and file.exists(): return file diff --git a/taipy/gui/gui.py b/taipy/gui/gui.py index 7e344bc772..c9488ddebd 100644 --- a/taipy/gui/gui.py +++ b/taipy/gui/gui.py @@ -1332,12 +1332,8 @@ def __send_ws_download(self, content: str, name: str, on_action: str) -> None: ) def __send_ws_alert( - self, type: str, - message: str, - system_notification: bool, - duration: int, - notification_id: t.Optional[str] = None - ) -> None: + self, type: str, message: str, system_notification: bool, duration: int, notification_id: t.Optional[str] = None + ) -> None: payload = { "type": _WsType.ALERT.value, "atype": type, @@ -2278,10 +2274,9 @@ def _close_notification( message="", # No need for a message when closing system_notification=False, # System notification not needed for closing duration=0, # No duration since it's an immediate close - notification_id=notification_id + notification_id=notification_id, ) - def _hold_actions( self, callback: t.Optional[t.Union[str, t.Callable]] = None, @@ -2661,13 +2656,13 @@ def __register_blueprint(self): s if bool(urlparse(s).netloc) else f"{Gui._EXTENSION_ROOT}/{name}/{s}{lib.get_query(s)}" for name, libs in Gui.__extensions.items() for lib in libs - for s in (lib.get_scripts() or []) + for s in (lib._do_get_relative_paths(lib.get_scripts())) ] styles = [ s if bool(urlparse(s).netloc) else f"{Gui._EXTENSION_ROOT}/{name}/{s}{lib.get_query(s)}" for name, libs in Gui.__extensions.items() for lib in libs - for s in (lib.get_styles() or []) + for s in (lib._do_get_relative_paths(lib.get_styles())) ] if self._get_config("stylekit", True): styles.append("stylekit/stylekit.css") diff --git a/taipy/gui_core/_GuiCoreLib.py b/taipy/gui_core/_GuiCoreLib.py index e9f5c2a435..1bf6fd9dbf 100644 --- a/taipy/gui_core/_GuiCoreLib.py +++ b/taipy/gui_core/_GuiCoreLib.py @@ -65,7 +65,7 @@ class _GuiCore(ElementLibrary): __DATANODE_SELECTOR_SORT_VAR = "__tpgc_dn_sort" __DATANODE_SELECTOR_ERROR_VAR = "__tpgc_dn_error" - __elts = { + __elements = { "scenario_selector": Element( "value", { @@ -314,10 +314,10 @@ def get_name(self) -> str: return _GuiCore.__LIB_NAME def get_elements(self) -> t.Dict[str, Element]: - return _GuiCore.__elts + return _GuiCore.__elements def get_scripts(self) -> t.List[str]: - return ["lib/taipy-gui-core.js"] + return ["lib/*.js"] def on_init(self, gui: Gui) -> t.Optional[t.Tuple[str, t.Any]]: self.ctx = _GuiCoreContext(gui)