|
@@ -13,12 +13,17 @@ import typing as t
|
|
|
from abc import ABC, abstractmethod
|
|
|
from os import path
|
|
|
|
|
|
+from charset_normalizer import detect
|
|
|
+
|
|
|
+from taipy.logger._taipy_logger import _TaipyLogger
|
|
|
+
|
|
|
from ..page import Page
|
|
|
from ..utils import _is_in_notebook, _varname_from_content
|
|
|
from ._html import _TaipyHTMLParser
|
|
|
|
|
|
if t.TYPE_CHECKING:
|
|
|
- from ..builder._element import _Element
|
|
|
+ from watchdog.observers import BaseObserverSubclassCallable
|
|
|
+
|
|
|
from ..gui import Gui
|
|
|
|
|
|
|
|
@@ -41,6 +46,7 @@ class _Renderer(Page, ABC):
|
|
|
self._content = ""
|
|
|
self._base_element: t.Optional[_Element] = None
|
|
|
self._filepath = ""
|
|
|
+ self._observer: t.Optional["BaseObserverSubclassCallable"] = None
|
|
|
if isinstance(content, str):
|
|
|
self.__process_content(content)
|
|
|
elif isinstance(content, _Element):
|
|
@@ -51,18 +57,37 @@ class _Renderer(Page, ABC):
|
|
|
)
|
|
|
|
|
|
def __process_content(self, content: str) -> None:
|
|
|
- if path.exists(content) and path.isfile(content):
|
|
|
- return self.__parse_file_content(content)
|
|
|
- if self._frame is not None:
|
|
|
- frame_dir_path = path.dirname(path.abspath(self._frame.f_code.co_filename))
|
|
|
- content_path = path.join(frame_dir_path, content)
|
|
|
- if path.exists(content_path) and path.isfile(content_path):
|
|
|
- return self.__parse_file_content(content_path)
|
|
|
+ relative_file_path = (
|
|
|
+ None if self._frame is None else path.join(path.dirname(self._frame.f_code.co_filename), content)
|
|
|
+ )
|
|
|
+ if relative_file_path is not None and path.exists(relative_file_path) and path.isfile(relative_file_path):
|
|
|
+ content = relative_file_path
|
|
|
+ if content == relative_file_path or (path.exists(content) and path.isfile(content)):
|
|
|
+ self.__parse_file_content(content)
|
|
|
+ # Watchdog observer: watch for file changes
|
|
|
+ if _is_in_notebook() and self._observer is None:
|
|
|
+ self.__observe_file_change(content)
|
|
|
+ return
|
|
|
self._content = content
|
|
|
|
|
|
+ def __observe_file_change(self, file_path: str):
|
|
|
+ from watchdog.observers import Observer
|
|
|
+
|
|
|
+ from .utils import FileWatchdogHandler
|
|
|
+
|
|
|
+ self._observer = Observer()
|
|
|
+ file_path = path.abspath(file_path)
|
|
|
+ self._observer.schedule(FileWatchdogHandler(file_path, self), path.dirname(file_path), recursive=False)
|
|
|
+ self._observer.start()
|
|
|
+
|
|
|
def __parse_file_content(self, content):
|
|
|
- with open(t.cast(str, content), "r") as f:
|
|
|
- self._content = f.read()
|
|
|
+ with open(t.cast(str, content), "rb") as f:
|
|
|
+ file_content = f.read()
|
|
|
+ encoding = "utf-8"
|
|
|
+ if (detected_encoding := detect(file_content)["encoding"]) is not None:
|
|
|
+ encoding = detected_encoding
|
|
|
+ _TaipyLogger._get_logger().info(f"Detected '{encoding}' encoding for file '{content}'.")
|
|
|
+ self._content = file_content.decode(encoding)
|
|
|
# Save file path for error handling
|
|
|
self._filepath = content
|
|
|
|
|
@@ -85,6 +110,11 @@ class _Renderer(Page, ABC):
|
|
|
if not _is_in_notebook():
|
|
|
raise RuntimeError("'set_content()' must be used in an IPython notebook context")
|
|
|
self.__process_content(content)
|
|
|
+ if self._notebook_gui is not None and self._notebook_page is not None:
|
|
|
+ if self._notebook_gui._config.root_page is self._notebook_page:
|
|
|
+ self._notebook_gui._navigate("/", {"tp_reload_all": "true"})
|
|
|
+ return
|
|
|
+ self._notebook_gui._navigate(self._notebook_page._route, {"tp_reload_same_route_only": "true"})
|
|
|
|
|
|
def _get_content_detail(self, gui: "Gui") -> str:
|
|
|
if self._filepath:
|