Skip to content

Commit

Permalink
Merge pull request #2359 from Avaiga/1808-allow-custom-javascript-files
Browse files Browse the repository at this point in the history
Allow custom Javascript files
  • Loading branch information
namnguyen20999 authored Dec 23, 2024
2 parents c9aff0e + 6837925 commit 4153273
Showing 1 changed file with 45 additions and 13 deletions.
58 changes: 45 additions & 13 deletions taipy/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def __init__(
env_filename: t.Optional[str] = None,
libraries: t.Optional[t.List[ElementLibrary]] = None,
flask: t.Optional[Flask] = None,
script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]] = None
):
"""Initialize a new Gui instance.
Expand Down Expand Up @@ -231,12 +232,19 @@ def __init__(
If this argument is set, this `Gui` instance will use the value of this argument
as the underlying server. If omitted or set to None, this `Gui` will create its
own Flask application instance and use it to serve the pages.
script_paths (Optional[Union[str, Path, List[Union[str, Path]]]]):
Specifies the path(s) to the JavaScript files or external resources used by the application.
It can be a single URL or path, or a list containing multiple URLs and/or paths.
"""
# store suspected local containing frame
self.__frame = t.cast(FrameType, t.cast(FrameType, currentframe()).f_back)
self.__default_module_name = _get_module_name_from_frame(self.__frame)
self._set_css_file(css_file)

# Set the assets URL path
self.__script_files: list[str] = []
self.__load_scripts(script_paths)

# Preserve server config for server initialization
if path_mapping is None:
path_mapping = {}
Expand Down Expand Up @@ -411,6 +419,30 @@ def __init__(
for library in libraries:
Gui.add_library(library)

def __load_scripts(self, script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]]):
if script_paths is None:
return
else:
if isinstance(script_paths, (str, Path)):
script_paths = [script_paths]

for script_path in script_paths:
if isinstance(script_path, str):
parsed_url = urlparse(script_path)
if parsed_url.netloc:
self.__script_files.append(script_path)
continue
script_path = Path(script_path)

if isinstance(script_path,
Path) and script_path.exists() and script_path.is_file() and script_path.suffix == ".js":
self.__script_files.append(str(script_path))
else:
_warn(f"Script path '{script_path}' does not exist, is not a file, or is not a JavaScript file.")

if not self.__script_files:
_warn("No JavaScript files found in the specified script paths.")

@staticmethod
def add_library(library: ElementLibrary) -> None:
"""Add a custom visual element library.
Expand Down Expand Up @@ -474,7 +506,6 @@ def __process_content_provider(self, state: State, path: str, query: t.Dict[str,
from plotly.graph_objs import Figure as PlotlyFigure # type: ignore[reportMissingImports]

if isinstance(content, PlotlyFigure):

def get_plotly_content(figure: PlotlyFigure):
return figure.to_html()

Expand All @@ -486,7 +517,6 @@ def get_plotly_content(figure: PlotlyFigure):
from matplotlib.figure import Figure as MatplotlibFigure

if isinstance(content, MatplotlibFigure):

def get_matplotlib_content(figure: MatplotlibFigure):
import base64
from io import BytesIO
Expand Down Expand Up @@ -800,12 +830,12 @@ def _get_real_var_name(self, var_name: str) -> t.Tuple[str, str]:
if var_name.startswith(_TaipyBase._HOLDER_PREFIX):
for hp in _TaipyBase._get_holder_prefixes():
if var_name.startswith(hp):
var_name = var_name[len(hp) :]
var_name = var_name[len(hp):]
break
suffix_var_name = ""
if "." in var_name:
first_dot_index = var_name.index(".")
suffix_var_name = var_name[first_dot_index + 1 :]
suffix_var_name = var_name[first_dot_index + 1:]
var_name = var_name[:first_dot_index]
var_name_decode, module_name = _variable_decode(self._get_expr_from_hash(var_name))
current_context = self._get_locals_context()
Expand Down Expand Up @@ -1202,8 +1232,7 @@ def __request_data_update(self, var_name: str, payload: t.Any) -> None:
if ret_payload:
break
except Exception as e: # pragma: no cover
_warn(
f"Exception raised in '{lib_name}.get_data({lib_name}, payload, {user_var_name}, value)'", # noqa: E501
_warn(f"Exception raised in '{lib_name}.get_data({lib_name}, payload, {user_var_name}, value)'", # noqa: E501
e,
)
if not isinstance(ret_payload, dict):
Expand Down Expand Up @@ -1277,9 +1306,9 @@ def __handle_ws_get_data_tree(self):
k: v
for k, v in vars(self._get_data_scope()).items()
if not k.startswith("_")
and not callable(v)
and "TpExPr" not in k
and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
and not callable(v)
and "TpExPr" not in k
and not isinstance(v, (ModuleType, FunctionType, LambdaType, type, Page))
}
function_data = {
k: v
Expand Down Expand Up @@ -2026,7 +2055,7 @@ def add_page(
raise Exception("name is required for add_page() function.")
if not Gui.__RE_PAGE_NAME.match(name): # pragma: no cover
raise SyntaxError(
f'Page name "{name}" is invalid. It must only contain letters, digits, dash (-), underscore (_), and forward slash (/) characters.' # noqa: E501
f'Page name "{name}" is invalid. It must only contain letters, digits, dash (-), underscore (_), and forward slash (/) characters.' # noqa: E501
)
if name.startswith("/"): # pragma: no cover
raise SyntaxError(f'Page name "{name}" cannot start with forward slash (/) character.')
Expand Down Expand Up @@ -2224,7 +2253,7 @@ def _bind_var(self, var_name: str) -> str:
self._bind(encoded_var_name, bind_locals[var_name])
else:
_warn(
f"Variable '{var_name}' is not available in either the '{self._get_locals_context()}' or '__main__' modules." # noqa: E501
f"Variable '{var_name}' is not available in either the '{self._get_locals_context()}' or '__main__' modules." # noqa: E501
)
return encoded_var_name

Expand Down Expand Up @@ -2593,7 +2622,7 @@ def _get_webapp_path(self):
_warn(f"Using webapp_path: '{_conf_webapp_path}'.")
else: # pragma: no cover
_warn(
f"webapp_path: '{_conf_webapp_path}' is not a valid directory. Falling back to '{_webapp_path}'." # noqa: E501
f"webapp_path: '{_conf_webapp_path}' is not a valid directory. Falling back to '{_webapp_path}'." # noqa: E501
)
return _webapp_path

Expand Down Expand Up @@ -2730,6 +2759,9 @@ def __register_blueprint(self):
if self.__css_file:
styles.append(f"{self.__css_file}")

if self.__script_files:
scripts.extend(self.__script_files)

self._flask_blueprint.append(extension_bp)

_webapp_path = self._get_webapp_path()
Expand Down Expand Up @@ -2905,7 +2937,7 @@ def run(
glob_ctx[lib_context[0]] = lib_context[1]
elif lib_context:
_warn(
f"Method {name}.on_init() should return a Tuple[str, Any] where the first element must be a valid Python identifier." # noqa: E501
f"Method {name}.on_init() should return a Tuple[str, Any] where the first element must be a valid Python identifier." # noqa: E501
)
except Exception as e: # pragma: no cover
if not self._call_on_exception(f"{name}.on_init", e):
Expand Down

0 comments on commit 4153273

Please sign in to comment.