dinhlongviolin1 hace 1 mes
padre
commit
c6e4a71ec6
Se han modificado 48 ficheros con 585 adiciones y 268 borrados
  1. 1 1
      taipy/_run.py
  2. 1 1
      taipy/gui/_default_config.py
  3. 2 2
      taipy/gui/config.py
  4. 3 6
      taipy/gui/custom/utils.py
  5. 134 101
      taipy/gui/gui.py
  6. 37 0
      taipy/gui/servers/__init__.py
  7. 14 0
      taipy/gui/servers/fastapi/__init__.py
  8. 44 0
      taipy/gui/servers/fastapi/server.py
  9. 14 0
      taipy/gui/servers/flask/__init__.py
  10. 17 38
      taipy/gui/servers/flask/server.py
  11. 18 0
      taipy/gui/servers/request.py
  12. 83 0
      taipy/gui/servers/server.py
  13. 90 0
      taipy/gui/servers/utils.py
  14. 6 5
      taipy/gui/state.py
  15. 15 9
      taipy/gui/utils/_locals_context.py
  16. 1 1
      taipy/gui/utils/proxy.py
  17. 5 5
      tests/gui/actions/test_download.py
  18. 2 2
      tests/gui/actions/test_get_module_context.py
  19. 1 1
      tests/gui/actions/test_get_state_id.py
  20. 1 1
      tests/gui/actions/test_get_user_content_url.py
  21. 2 2
      tests/gui/actions/test_hold_control.py
  22. 2 2
      tests/gui/actions/test_invoke_callback.py
  23. 2 2
      tests/gui/actions/test_navigate.py
  24. 4 4
      tests/gui/actions/test_notify.py
  25. 2 2
      tests/gui/actions/test_resume_control.py
  26. 1 1
      tests/gui/config/test_cli.py
  27. 34 33
      tests/gui/data/test_pandas_data_accessor.py
  28. 5 3
      tests/gui/gui_specific/test_broadcast.py
  29. 1 2
      tests/gui/gui_specific/test_favicon.py
  30. 13 10
      tests/gui/gui_specific/test_gui.py
  31. 1 1
      tests/gui/gui_specific/test_locals_context.py
  32. 2 2
      tests/gui/gui_specific/test_navigate.py
  33. 2 6
      tests/gui/gui_specific/test_state.py
  34. 1 1
      tests/gui/gui_specific/test_variable_binding.py
  35. 1 1
      tests/gui/gui_specific/test_variable_directory.py
  36. 1 1
      tests/gui/helpers.py
  37. 1 1
      tests/gui/long_runnig/test_long_running.py
  38. 4 4
      tests/gui/server/http/test_file_upload.py
  39. 1 1
      tests/gui/server/ws/test_a.py
  40. 1 1
      tests/gui/server/ws/test_broadcast.py
  41. 1 1
      tests/gui/server/ws/test_df.py
  42. 1 1
      tests/gui/server/ws/test_du.py
  43. 2 2
      tests/gui/server/ws/test_on_change.py
  44. 1 1
      tests/gui/server/ws/test_ru.py
  45. 1 1
      tests/gui/server/ws/test_u.py
  46. 2 2
      tests/gui/server/ws/test_with.py
  47. 6 6
      tests/gui/utils/test_evaluator.py
  48. 1 1
      tests/gui/utils/test_map_dict.py

+ 1 - 1
taipy/_run.py

@@ -57,7 +57,7 @@ def _run(*services: _AppType, **kwargs) -> t.Optional[Flask]:
         return None
 
     if gui and rest:
-        gui._set_flask(rest._app)  # type: ignore[union-attr]
+        gui._set_web_server(rest._app)  # type: ignore[union-attr]
         return gui.run(**kwargs)
     else:
         app = rest or gui

+ 1 - 1
taipy/gui/_default_config.py

@@ -53,7 +53,7 @@ default_config: Config = {
     "debug": False,
     "extended_status": False,
     "favicon": None,
-    "flask_log": False,
+    "server_log": False,
     "host": "127.0.0.1",
     "light_theme": None,
     "margin": "1em",

+ 2 - 2
taipy/gui/config.py

@@ -42,7 +42,7 @@ ConfigParameter = t.Literal[
     "debug",
     "extended_status",
     "favicon",
-    "flask_log",
+    "server_log",
     "host",
     "light_theme",
     "margin",
@@ -117,7 +117,7 @@ Config = t.TypedDict(
         "debug": bool,
         "extended_status": bool,
         "favicon": t.Optional[str],
-        "flask_log": bool,
+        "server_log": bool,
         "host": str,
         "light_theme": t.Optional[t.Dict[str, t.Any]],
         "margin": t.Optional[str],

+ 3 - 6
taipy/gui/custom/utils.py

@@ -12,10 +12,7 @@
 import contextlib
 import typing as t
 
-from flask import has_request_context
-from flask.globals import request
-
-from ..server import _Server
+from ..servers import _Server, get_request, has_request_context
 from ._page import ResourceHandler, _ExternalResourceHandlerManager
 
 
@@ -24,7 +21,7 @@ def is_in_custom_page_context() -> bool:
     resource_handler_id = None
     with contextlib.suppress(Exception):
         if has_request_context():
-            resource_handler_id = request.cookies.get(_Server._RESOURCE_HANDLER_ARG, None)
+            resource_handler_id = get_request().cookies.get(_Server._RESOURCE_HANDLER_ARG, None)
     return resource_handler_id is not None
 
 
@@ -33,5 +30,5 @@ def get_current_resource_handler() -> t.Optional[ResourceHandler]:
     resource_handler_id = None
     with contextlib.suppress(Exception):
         if has_request_context():
-            resource_handler_id = request.cookies.get(_Server._RESOURCE_HANDLER_ARG, None)
+            resource_handler_id = get_request().cookies.get(_Server._RESOURCE_HANDLER_ARG, None)
     return _ExternalResourceHandlerManager().get(resource_handler_id) if resource_handler_id else None

+ 134 - 101
taipy/gui/gui.py

@@ -32,16 +32,8 @@ from urllib.parse import unquote, urlencode, urlparse
 
 import markdown as md_lib
 import tzlocal
-from flask import (
-    Blueprint,
-    Flask,
-    g,
-    has_app_context,
-    jsonify,
-    request,
-    send_file,
-    send_from_directory,
-)
+from fastapi import FastAPI
+from flask import Blueprint, Flask
 from werkzeug.utils import secure_filename
 
 import __main__  # noqa: F401
@@ -71,7 +63,18 @@ from .data.data_scope import _DataScopes
 from .extension.library import Element, ElementLibrary
 from .page import Page
 from .partial import Partial
-from .server import _Server
+from .servers import (
+    ServerFrameworks,
+    _Server,
+    create_server,
+    get_request,
+    get_request_meta,
+    get_server_type,
+    has_server_context,
+    send_file,
+    send_from_directory,
+    set_server_type,
+)
 from .state import State, _AsyncState, _GuiState
 from .types import _WsType
 from .utils import (
@@ -185,8 +188,8 @@ class Gui:
         path_mapping: t.Optional[dict] = None,
         env_filename: t.Optional[str] = None,
         libraries: t.Optional[t.List[ElementLibrary]] = None,
-        flask: t.Optional[Flask] = None,
         script_paths: t.Union[str, Path, t.List[t.Union[str, Path]], None] = None,
+        server: t.Union[ServerFrameworks, Flask, FastAPI] = "flask",
     ):
         """Initialize a new Gui instance.
 
@@ -227,13 +230,11 @@ class Gui:
                 instances that pages can reference.<br/>
                 Using this argument is equivalent to calling `(Gui.)add_library()^` for each
                 list's elements.
-            flask (Optional[Flask]): An optional instance of a Flask application object.<br/>
-                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 (Union[str, Path, List[Union[str, Path]], None]):
                 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.
+            server (Union[ServerFrameworks, Flask, FastAPI]): The server type to use for the application.<br/>
+                The default value is `flask`.<br/>
         """
         # store suspected local containing frame
         self.__frame = t.cast(FrameType, t.cast(FrameType, currentframe()).f_back)
@@ -248,7 +249,21 @@ class Gui:
         if path_mapping is None:
             path_mapping = {}
         self._path_mapping = path_mapping
-        self._flask = flask
+
+        # Server config
+        self._server_instance: t.Union[Flask, FastAPI, None] = None
+        if isinstance(server, Flask):
+            set_server_type("flask")
+            self._server_instance = server
+        elif isinstance(server, FastAPI):
+            set_server_type("fastapi")
+            self._server_instance = server
+        elif isinstance(server, str) and server in t.get_args(ServerFrameworks):
+            set_server_type(server)  # type: ignore[arg-type]
+        else:
+            raise ValueError(
+                f"Invalid 'server' option. 'server' must be one of {t.get_args(ServerFrameworks)} or a Flask or FastAPI instance."  # noqa: E501
+            )
 
         self._config = _Config()
         self.__content_accessor = None
@@ -374,7 +389,6 @@ class Gui:
         self.__client_id_2_sid: t.Dict[str, t.Set[str]] = {}
 
         # Load default config
-        self._flask_blueprint: t.List[Blueprint] = []
         self._config._load(default_config)
 
         # get taipy version
@@ -625,35 +639,35 @@ class Gui:
         return (
             _DataScopes._GLOBAL_ID
             if self._bindings()._is_single_client()
-            else getattr(g, Gui.__ARG_CLIENT_ID, "unknown id")
+            else getattr(get_request_meta(), Gui.__ARG_CLIENT_ID, "unknown id")
         )
 
     def __set_client_id_in_context(self, client_id: t.Optional[str] = None, force=False):
-        if not client_id and request:
-            client_id = request.args.get(Gui.__ARG_CLIENT_ID, "")
-        if not client_id and (ws_client_id := getattr(g, "ws_client_id", None)):
+        if not client_id and get_request():
+            client_id = get_request().args.get(Gui.__ARG_CLIENT_ID, "")
+        if not client_id and (ws_client_id := getattr(get_request_meta(), "ws_client_id", None)):
             client_id = ws_client_id
         if not client_id and force:
             res = self._bindings()._get_or_create_scope("")
             client_id = res[0] if res[1] else None
-        if client_id and request:
-            if sid := getattr(request, "sid", None):
+        if client_id and get_request():
+            if sid := getattr(get_request(), "sid", None):
                 sids = self.__client_id_2_sid.get(client_id, None)
                 if sids is None:
                     sids = set()
                     self.__client_id_2_sid[client_id] = sids
                 sids.add(sid)
-        g.client_id = client_id
+        get_request_meta().client_id = client_id
 
     def __is_var_modified_in_context(self, var_name: str, derived_vars: t.Set[str]) -> bool:
-        modified_vars: t.Optional[t.Set[str]] = getattr(g, "modified_vars", None)
-        der_vars: t.Optional[t.Set[str]] = getattr(g, "derived_vars", None)
-        setattr(g, "update_count", getattr(g, "update_count", 0) + 1)  # noqa: B010
+        modified_vars: t.Optional[t.Set[str]] = getattr(get_request_meta(), "modified_vars", None)
+        der_vars: t.Optional[t.Set[str]] = getattr(get_request_meta(), "derived_vars", None)
+        setattr(get_request_meta(), "update_count", getattr(get_request_meta(), "update_count", 0) + 1)  # noqa: B010
         if modified_vars is None:
             modified_vars = set()
-            g.modified_vars = modified_vars
+            get_request_meta().modified_vars = modified_vars
         if der_vars is None:
-            g.derived_vars = derived_vars
+            get_request_meta().derived_vars = derived_vars
         else:
             der_vars.update(derived_vars)
         if var_name in modified_vars:
@@ -662,15 +676,15 @@ class Gui:
         return False
 
     def __clean_vars_on_exit(self) -> t.Optional[t.Set[str]]:
-        update_count = getattr(g, "update_count", 0) - 1
+        update_count = getattr(get_request_meta(), "update_count", 0) - 1
         if update_count < 1:
-            derived_vars: t.Set[str] = getattr(g, "derived_vars", set())
-            delattr(g, "update_count")
-            delattr(g, "modified_vars")
-            delattr(g, "derived_vars")
+            derived_vars: t.Set[str] = getattr(get_request_meta(), "derived_vars", set())
+            delattr(get_request_meta(), "update_count")
+            delattr(get_request_meta(), "modified_vars")
+            delattr(get_request_meta(), "derived_vars")
             return derived_vars
         else:
-            setattr(g, "update_count", update_count)  # noqa: B010
+            setattr(get_request_meta(), "update_count", update_count)  # noqa: B010
             return None
 
     def _handle_connect(self):
@@ -678,7 +692,9 @@ class Gui:
 
     def _handle_disconnect(self):
         _Hooks()._handle_disconnect(self)
-        if (sid := getattr(request, "sid", None)) and (st_to := self._get_config("state_retention_period", 0)) > 0:
+        if (sid := getattr(get_request(), "sid", None)) and (
+            st_to := self._get_config("state_retention_period", 0)
+        ) > 0:
             for cl_id, sids in self.__client_id_2_sid.items():
                 if sid in sids:
                     if len(sids) == 1:
@@ -695,7 +711,7 @@ class Gui:
             except Exception as e:
                 _warn(f"Unexpected error removing state {client_id}", e)
 
-    def _manage_message(self, msg_type: _WsType, message: dict) -> None:
+    def _manage_ws_message(self, msg_type: _WsType, message: dict) -> None:
         try:
             client_id = None
             if msg_type == _WsType.CLIENT_ID.value:
@@ -710,7 +726,7 @@ class Gui:
                         self.__handle_ws_app_id({"name": message.get("name"), "payload": front_app_id})
             expected_client_id = client_id or message.get(Gui.__ARG_CLIENT_ID)
             self.__set_client_id_in_context(expected_client_id)
-            g.ws_client_id = expected_client_id
+            get_request_meta().ws_client_id = expected_client_id
             with self._set_locals_context(message.get("module_context") or None):
                 with self._get_authorization():
                     payload = message.get("payload", {})
@@ -903,7 +919,7 @@ class Gui:
         if len(parts) > 1:
             file_name = parts[-1]
             (dir_path, as_attachment) = self.__get_content_accessor().get_content_path(
-                path[: -len(file_name) - 1], file_name, request.args.get("bypass")
+                path[: -len(file_name) - 1], file_name, get_request().args.get("bypass")
             )
             if dir_path:
                 return send_from_directory(str(dir_path), file_name, as_attachment=as_attachment)
@@ -919,7 +935,7 @@ class Gui:
     def __serve_user_content(self, path: str) -> t.Any:
         self.__set_client_id_in_context()
         q_args: t.Dict[str, str] = {}
-        q_args.update(request.args)
+        q_args.update(get_request().args)
         q_args.pop(Gui.__ARG_CLIENT_ID, None)
         cb_function: t.Union[t.Callable, str, None] = None
         cb_function_name = None
@@ -1028,17 +1044,17 @@ class Gui:
 
     def __upload_files(self):
         self.__set_client_id_in_context()
-        on_upload_action = request.form.get("on_action", None)
-        var_name = t.cast(str, request.form.get("var_name", None))
+        on_upload_action = get_request().form.get("on_action", None)
+        var_name = t.cast(str, get_request().form.get("var_name", None))
         if not var_name and not on_upload_action:
             _warn("upload files: No var name")
             return ("upload files: No var name", 400)
-        context = request.form.get("context", None)
-        upload_data = request.form.get("upload_data", None)
-        multiple = "multiple" in request.form and request.form["multiple"] == "True"
+        context = get_request().form.get("context", None)
+        upload_data = get_request().form.get("upload_data", None)
+        multiple = "multiple" in get_request().form and get_request().form["multiple"] == "True"
 
         # File parsing and checks
-        file = request.files.get("blob", None)
+        file = get_request().files.get("blob", None)
         if not file:
             _warn("upload files: No file part")
             return ("upload files: No file part", 400)
@@ -1049,15 +1065,15 @@ class Gui:
             return ("upload files: No selected file", 400)
 
         # Path parsing and checks
-        path = request.form.get("path", "")
+        path = get_request().form.get("path", "")
         suffix = ""
         complete = True
         part = 0
 
-        if "total" in request.form:
-            total = int(request.form["total"])
-            if total > 1 and "part" in request.form:
-                part = int(request.form["part"])
+        if "total" in get_request().form:
+            total = int(get_request().form["total"])
+            if total > 1 and "part" in get_request().form:
+                part = int(get_request().form["part"])
                 suffix = f".part.{part}"
                 complete = part == total - 1
 
@@ -1405,8 +1421,7 @@ class Gui:
         grouping_message = self.__get_message_grouping() if allow_grouping else None
         if grouping_message is None:
             try:
-                self._server._ws.emit(
-                    "message",
+                self._server.send_ws_message(
                     payload,
                     to=t.cast(str, self.__get_ws_receiver(send_back_only)),
                 )
@@ -1419,7 +1434,7 @@ class Gui:
     def __broadcast_ws(self, payload: dict, client_id: t.Optional[str] = None):
         try:
             to = list(self.__get_sids(client_id)) if client_id else []
-            self._server._ws.emit("message", payload, to=t.cast(str, to) if to else None, include_self=True)
+            self._server.send_ws_message(payload, to=t.cast(str, to) if to else None, include_self=True)
             time.sleep(0.001)
         except Exception as e:  # pragma: no cover
             _warn(f"Exception raised in WebSocket communication in '{self.__frame.f_code.co_name}'", e)
@@ -1427,8 +1442,7 @@ class Gui:
     def __send_ack(self, ack_id: t.Optional[str]) -> None:
         if ack_id:
             try:
-                self._server._ws.emit(
-                    "message",
+                self._server.send_ws_message(
                     {"type": _WsType.ACKNOWLEDGEMENT.value, "id": ack_id},
                     to=t.cast(str, self.__get_ws_receiver(True)),
                 )
@@ -1539,7 +1553,7 @@ class Gui:
     def __get_ws_receiver(self, send_back_only=False) -> t.Union[t.List[str], t.Any, None]:
         if self._bindings()._is_single_client():
             return None
-        sid = getattr(request, "sid", None) if request else None
+        sid = getattr(get_request(), "sid", None) if get_request() else None
         sids = self.__get_sids(self._get_client_id())
         if sid:
             sids.add(sid)
@@ -1705,13 +1719,13 @@ class Gui:
             module_context (Optional[str]): The name of the module that will be used.
         """  # noqa: E501
         this_sid = None
-        if request:
+        if get_request():
             # avoid messing with the client_id => Set(ws id)
-            this_sid = getattr(request, "sid", None)
-            request.sid = None  # type: ignore[attr-defined]
+            this_sid = getattr(get_request(), "sid", None)
+            get_request().sid = None  # type: ignore[attr-defined]
         try:
-            with self.get_flask_app().app_context():
-                setattr(g, Gui.__ARG_CLIENT_ID, state_id)
+            with self.get_server_instance().app_context():
+                setattr(get_request_meta(), Gui.__ARG_CLIENT_ID, state_id)
                 with self._set_module_context(module_context):
                     if not _is_function(callback):
                         callback = self._get_user_function(t.cast(str, callback))
@@ -1727,7 +1741,7 @@ class Gui:
                 )
         finally:
             if this_sid:
-                request.sid = this_sid  # type: ignore[attr-defined]
+                get_request().sid = this_sid  # type: ignore[attr-defined]
         return None
 
     def broadcast_callback(
@@ -1798,7 +1812,7 @@ class Gui:
 
     def _is_in_brdcst_callback(self):
         try:
-            return getattr(g, Gui.__BRDCST_CALLBACK_G_ID, False)
+            return getattr(get_request_meta(), Gui.__BRDCST_CALLBACK_G_ID, False)
         except RuntimeError:
             return False
 
@@ -2069,8 +2083,14 @@ class Gui:
     def _get_root_page_name():
         return Gui.__root_page_name
 
+    # Deprecated
     def _set_flask(self, flask: Flask):
-        self._flask = flask
+        raise RuntimeError(
+            "'_set_flask()' is deprecated. Use '_set_web_server()' instead as multiple web frameworks has been supported."  # noqa: E501
+        )
+
+    def _set_web_server(self, server: t.Union[Flask, FastAPI]):
+        self._server_instance = server
 
     def _get_default_module_name(self):
         return self.__default_module_name
@@ -2378,11 +2398,11 @@ class Gui:
 
     def _set_broadcast(self, broadcast: bool = True):
         with contextlib.suppress(RuntimeError):
-            setattr(g, Gui.__BROADCAST_G_ID, broadcast)
+            setattr(get_request_meta(), Gui.__BROADCAST_G_ID, broadcast)
 
     def _is_broadcasting(self) -> bool:
         try:
-            return getattr(g, Gui.__BROADCAST_G_ID, False)
+            return getattr(get_request_meta(), Gui.__BROADCAST_G_ID, False)
         except RuntimeError:
             return False
 
@@ -2549,7 +2569,7 @@ class Gui:
         nav_page = page_name
         if hasattr(self, "on_navigate") and _is_function(self.on_navigate):
             try:
-                params = request.args.to_dict() if hasattr(request, "args") else {}
+                params = get_request().args.to_dict() if hasattr(get_request(), "args") else {}
                 params.pop("client_id", None)
                 params.pop("v", None)
                 nav_page = self._call_function_with_state(
@@ -2589,7 +2609,7 @@ class Gui:
         """Handle the bindings of custom page variables"""
         if not isinstance(page, CustomPage):
             return
-        with self.get_flask_app().app_context() if has_app_context() else contextlib.nullcontext():  # type: ignore[attr-defined]
+        with self.get_server_instance().app_context() if has_server_context() else contextlib.nullcontext():  # type: ignore[attr-defined]
             self.__set_client_id_in_context(client_id)
             with self._set_locals_context(page._get_module_name()):
                 for k, v in self._get_locals_bind().items():
@@ -2612,7 +2632,7 @@ class Gui:
         # Make sure that there is a page instance found
         if page is None:
             return (
-                jsonify({"error": f"Page '{nav_page}' doesn't exist."}),
+                self._server.direct_render_json({"error": f"Page '{nav_page}' doesn't exist."}),
                 400,
                 {"Content-Type": "application/json; charset=utf-8"},
             )
@@ -2640,7 +2660,7 @@ class Gui:
         if page._rendered_jsx is not None:
             with self._set_locals_context(context):
                 self._call_on_page_load(nav_page)
-            return self._server._render(
+            return self._server.render(
                 page._rendered_jsx,
                 page._script_paths if page._script_paths is not None else [],
                 page._style if page._style is not None else "",
@@ -2651,7 +2671,7 @@ class Gui:
             return ("No page template", 404)
 
     def _render_route(self) -> t.Any:
-        return self._server._direct_render_json(
+        return self._server.direct_render_json(
             {
                 "locations": {
                     "/" if route == Gui.__root_page_name else f"/{route}": f"/{route}" for route in self._config.routes
@@ -2660,17 +2680,20 @@ class Gui:
             }
         )
 
-    def get_flask_app(self) -> Flask:
-        """Get the internal Flask application.
+    def get_flask_app(self):
+        raise RuntimeError("'get_flask_app()' is deprecated. Use 'get_server_instance()' instead.")
+
+    def get_server_instance(self) -> t.Union[Flask, FastAPI]:
+        """Get the internal server application.
 
         This method must be called **after** `(Gui.)run()^` was invoked.
 
         Returns:
-            The Flask instance used.
+            The server instance used.
         """
         if hasattr(self, "_server"):
-            return t.cast(Flask, self._server.get_flask())
-        raise RuntimeError("get_flask_app() cannot be invoked before run() has been called.")
+            return t.cast(Flask, self._server.get_server_instance())
+        raise RuntimeError("get_server_instance() cannot be invoked before run() has been called.")
 
     def _get_port(self) -> int:
         return self._server.get_port()
@@ -2745,10 +2768,10 @@ class Gui:
         app_config = self._config.config
         # Init server if there is no server
         if not hasattr(self, "_server"):
-            self._server = _Server(
+            self._server = create_server(
                 self,  # type: ignore[arg-type]
                 path_mapping=self._path_mapping,
-                flask=self._flask,
+                flask=self._server_instance,
                 async_mode=app_config.get("async_mode"),
                 allow_upgrades=not app_config.get("notebook_proxy"),
                 server_config=app_config.get("server_config"),
@@ -2757,11 +2780,10 @@ class Gui:
         # Stop and reinitialize the server if it is still running as a thread
         if (_is_in_notebook() or app_config.get("run_in_thread")) and hasattr(self._server, "_thread"):
             self.stop()
-            self._flask_blueprint = []
-            self._server = _Server(
+            self._server = create_server(
                 self,  # type: ignore[arg-type]
                 path_mapping=self._path_mapping,
-                flask=self._flask,
+                flask=self._server_instance,
                 async_mode=app_config.get("async_mode"),
                 allow_upgrades=not app_config.get("notebook_proxy"),
                 server_config=app_config.get("server_config"),
@@ -2785,7 +2807,7 @@ class Gui:
             _TaipyLogger._get_logger().info(f" * NGROK Public Url: {self._ngrok[0].public_url}")
 
     def __bind_default_function(self):
-        with self.get_flask_app().app_context():
+        with self.get_server_instance().app_context():
             if additional_pages := _Hooks()._get_additional_pages():
                 # add page context for additional pages so that they can be managed by the variable directory
                 for page in additional_pages:
@@ -2803,6 +2825,14 @@ class Gui:
             self.__bind_local_func("on_user_content")
 
     def __register_blueprint(self):
+        server_type = get_server_type()
+        if server_type == "flask":
+            self.__register_flask_blueprint()
+        if server_type == "fastapi":
+            self.__register_fastapi_blueprint()
+
+    def __register_flask_blueprint(self):
+        flask_blueprint: t.List[Blueprint] = []
         # add en empty main page if it is not defined
         if Gui.__root_page_name not in self._config.routes:
             new_page = _Page()
@@ -2812,22 +2842,22 @@ class Gui:
             self._config.routes.append(Gui.__root_page_name)
 
         pages_bp = Blueprint("taipy_pages", __name__)
-        self._flask_blueprint.append(pages_bp)
+        flask_blueprint.append(pages_bp)
 
         # server URL Rule for taipy images
         images_bp = Blueprint("taipy_images", __name__)
         images_bp.add_url_rule(f"/{Gui.__CONTENT_ROOT}/<path:path>", view_func=self.__serve_content)
-        self._flask_blueprint.append(images_bp)
+        flask_blueprint.append(images_bp)
 
         # server URL for uploaded files
         upload_bp = Blueprint("taipy_upload", __name__)
         upload_bp.add_url_rule(f"/{Gui.__UPLOAD_URL}", view_func=self.__upload_files, methods=["POST"])
-        self._flask_blueprint.append(upload_bp)
+        flask_blueprint.append(upload_bp)
 
         # server URL for user content
         user_content_bp = Blueprint("taipy_user_content", __name__)
         user_content_bp.add_url_rule(f"/{Gui.__USER_CONTENT_URL}/<path:path>", view_func=self.__serve_user_content)
-        self._flask_blueprint.append(user_content_bp)
+        flask_blueprint.append(user_content_bp)
 
         # server URL for extension resources
         extension_bp = Blueprint("taipy_extensions", __name__)
@@ -2854,11 +2884,11 @@ class Gui:
         if self.__script_files:
             scripts.extend(self.__script_files)
 
-        self._flask_blueprint.append(extension_bp)
+        flask_blueprint.append(extension_bp)
 
         _webapp_path = self._get_webapp_path()
 
-        self._flask_blueprint.append(
+        flask_blueprint.append(
             self._server._get_default_blueprint(
                 static_folder=_webapp_path,
                 template_folder=_webapp_path,
@@ -2884,8 +2914,11 @@ class Gui:
         _Hooks()._add_external_blueprint(self, __name__)
 
         # Register Flask Blueprint if available
-        for bp in self._flask_blueprint:
-            t.cast(Flask, self._server.get_flask()).register_blueprint(bp)
+        for bp in flask_blueprint:
+            t.cast(Flask, self._server.get_server_instance()).register_blueprint(bp)
+
+    def __register_fastapi_blueprint(self):
+        pass
 
     def _get_accessor(self):
         if self.__accessors is None:
@@ -2898,7 +2931,7 @@ class Gui:
         run_in_thread: bool = False,
         async_mode: str = "gevent",
         **kwargs,
-    ) -> t.Optional[Flask]:
+    ) -> t.Union[Flask, FastAPI, None]:
         """Start the server that delivers pages to web clients.
 
         Once you enter `run()`, users can run web browsers and point to the web server
@@ -3052,7 +3085,7 @@ class Gui:
 
         # Start Flask Server
         if not run_server:
-            return self.get_flask_app()
+            return self.get_server_instance()
 
         return self._server.run(
             host=app_config.get("host"),
@@ -3060,7 +3093,7 @@ class Gui:
             client_url=app_config.get("client_url"),
             debug=app_config.get("debug"),
             use_reloader=app_config.get("use_reloader"),
-            flask_log=app_config.get("flask_log"),
+            server_log=app_config.get("server_log"),
             run_in_thread=app_config.get("run_in_thread"),
             allow_unsafe_werkzeug=app_config.get("allow_unsafe_werkzeug"),
             notebook_proxy=app_config.get("notebook_proxy"),
@@ -3147,16 +3180,16 @@ class Gui:
         self, event_name: str, client_id: t.Optional[str] = None, payload: t.Optional[t.Dict[str, t.Any]] = None
     ):
         this_sid = None
-        if request:
+        if get_request():
             # avoid messing with the client_id => Set(ws id)
-            this_sid = getattr(request, "sid", None)
-            request.sid = None  # type: ignore[attr-defined]
+            this_sid = getattr(get_request(), "sid", None)
+            get_request().sid = None  # type: ignore[attr-defined]
 
         try:
-            with self.get_flask_app().app_context(), self.__event_manager:
+            with self.get_server_instance().app_context(), self.__event_manager:
                 if client_id:
-                    setattr(g, Gui.__ARG_CLIENT_ID, client_id)
+                    setattr(get_request_meta(), Gui.__ARG_CLIENT_ID, client_id)
                 _Hooks()._fire_event(event_name, client_id, payload)
         finally:
             if this_sid:
-                request.sid = this_sid  # type: ignore[attr-defined]
+                get_request().sid = this_sid  # type: ignore[attr-defined]

+ 37 - 0
taipy/gui/servers/__init__.py

@@ -0,0 +1,37 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from .server import ServerFrameworks, _Server
+from .utils import (
+    create_server,
+    get_request,
+    get_request_meta,
+    get_server_type,
+    has_request_context,
+    has_server_context,
+    send_file,
+    send_from_directory,
+    set_server_type,
+)
+
+__all__ = [
+    "_Server",
+    "create_server",
+    "get_request",
+    "get_request_meta",
+    "get_server_type",
+    "has_request_context",
+    "has_server_context",
+    "send_file",
+    "send_from_directory",
+    "set_server_type",
+    "ServerFrameworks",
+]

+ 14 - 0
taipy/gui/servers/fastapi/__init__.py

@@ -0,0 +1,14 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from .server import FastAPIServer
+
+__all__ = ["FastAPIServer"]

+ 44 - 0
taipy/gui/servers/fastapi/server.py

@@ -0,0 +1,44 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from ..server import _Server
+
+
+class FastAPIServer(_Server):
+    def get_server_instance(self):
+        raise NotImplementedError
+
+    def get_port(self) -> int:
+        raise NotImplementedError
+
+    def send_ws_message(self, *args, **kwargs):
+        raise NotImplementedError
+
+    def direct_render_json(self, data):
+        raise NotImplementedError
+
+    def run(
+        self,
+        host,
+        port,
+        client_url,
+        debug,
+        use_reloader,
+        server_log,
+        run_in_thread,
+        allow_unsafe_werkzeug,
+        notebook_proxy,
+        port_auto_ranges,
+    ):
+        raise NotImplementedError
+
+    def stop_thread(self):
+        raise NotImplementedError

+ 14 - 0
taipy/gui/servers/flask/__init__.py

@@ -0,0 +1,14 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from .server import FlaskServer
+
+__all__ = ["FlaskServer"]

+ 17 - 38
taipy/gui/server.py → taipy/gui/servers/flask/server.py

@@ -15,7 +15,6 @@ import contextlib
 import logging
 import os
 import pathlib
-import re
 import sys
 import time
 import typing as t
@@ -43,23 +42,17 @@ from werkzeug.serving import is_running_from_reloader
 import __main__
 from taipy.common.logger._taipy_logger import _TaipyLogger
 
-from ._renderers.json import _TaipyJsonProvider
-from .config import ServerConfig
-from .custom._page import _ExternalResourceHandlerManager
-from .utils import _is_in_notebook, _is_port_open, _RuntimeManager
-from .utils._css import get_style
+from ..._renderers.json import _TaipyJsonProvider
+from ...config import ServerConfig
+from ...custom._page import _ExternalResourceHandlerManager
+from ...utils import _is_in_notebook, _is_port_open, _RuntimeManager
+from ..server import _Server
 
 if t.TYPE_CHECKING:
-    from .gui import Gui
+    from ...gui import Gui
 
 
-class _Server:
-    __RE_OPENING_CURLY = re.compile(r"([^\"])(\{)")
-    __RE_CLOSING_CURLY = re.compile(r"(\})([^\"])")
-    __OPENING_CURLY = r"\1&#x7B;"
-    __CLOSING_CURLY = r"&#x7D;\2"
-    _RESOURCE_HANDLER_ARG = "tprh"
-
+class FlaskServer(_Server):
     def __init__(
         self,
         gui: Gui,
@@ -119,7 +112,7 @@ class _Server:
             if "status" in message:
                 _TaipyLogger._get_logger().info(message["status"])
             elif "type" in message:
-                gui._manage_message(message["type"], message)  # type: ignore[attr-defined]
+                gui._manage_ws_message(message["type"], message)  # type: ignore[attr-defined]
 
         @self._ws.on("connect")
         def handle_connect():
@@ -201,7 +194,7 @@ class _Server:
                     ) from None
 
             if path == "taipy.status.json":
-                return self._direct_render_json(self._gui._serve_status(pathlib.Path(template_folder) / path))  # type: ignore[attr-defined]
+                return self.direct_render_json(self._gui._serve_status(pathlib.Path(template_folder) / path))  # type: ignore[attr-defined]
             if (file_path := str(os.path.normpath((base_path := static_folder + os.path.sep) + path))).startswith(
                 base_path
             ) and os.path.isfile(file_path):
@@ -239,27 +232,10 @@ class _Server:
 
         return taipy_bp
 
-    # Update to render as JSX
-    def _render(self, html_fragment, script_paths, style, head, context):
-        template_str = _Server.__RE_OPENING_CURLY.sub(_Server.__OPENING_CURLY, html_fragment)
-        template_str = _Server.__RE_CLOSING_CURLY.sub(_Server.__CLOSING_CURLY, template_str)
-        template_str = template_str.replace('"{!', "{")
-        template_str = template_str.replace('!}"', "}")
-        style = get_style(style)
-        return self._direct_render_json(
-            {
-                "jsx": template_str,
-                "style": (style + os.linesep) if style else "",
-                "head": head or [],
-                "context": context or self._gui._get_default_module_name(),  # type: ignore[attr-defined]
-                "scriptPaths": script_paths,
-            }
-        )
-
-    def _direct_render_json(self, data):
+    def direct_render_json(self, data):
         return jsonify(data)
 
-    def get_flask(self):
+    def get_server_instance(self):
         return self._flask
 
     def get_port(self):
@@ -301,6 +277,9 @@ class _Server:
             if port not in _RuntimeManager().get_used_port() and not _is_port_open(self._host, port):
                 return port
 
+    def send_ws_message(self, *args, **kwargs):
+        self._ws.emit("message", *args, **kwargs)
+
     def run(
         self,
         host,
@@ -308,7 +287,7 @@ class _Server:
         client_url,
         debug,
         use_reloader,
-        flask_log,
+        server_log,
         run_in_thread,
         allow_unsafe_werkzeug,
         notebook_proxy,
@@ -321,7 +300,7 @@ class _Server:
         server_url = f"http://{host_value}:{port}"
         self._port = port
         if _is_in_notebook() and notebook_proxy:  # pragma: no cover
-            from .utils.proxy import NotebookProxy
+            from ...utils.proxy import NotebookProxy
 
             # Start proxy if not already started
             self._proxy = NotebookProxy(gui=self._gui, listening_port=port)
@@ -334,7 +313,7 @@ class _Server:
             raise ConnectionError(
                 f"Port {port} is already opened on {host} because another application is running on the same port.\nPlease pick another port number and rerun with the 'port=<new_port>' setting.\nYou can also let Taipy choose a port number for you by running with the 'port=\"auto\"' setting."  # noqa: E501
             )
-        if not flask_log:
+        if not server_log:
             log = logging.getLogger("werkzeug")
             log.disabled = True
             if not is_running_from_reloader():

+ 18 - 0
taipy/gui/servers/request.py

@@ -0,0 +1,18 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from contextvars import ContextVar
+
+from flask import Request
+from flask.ctx import _AppCtxGlobals
+
+request: ContextVar[Request] = ContextVar("request")
+request_meta: ContextVar[_AppCtxGlobals] = ContextVar("request_meta")

+ 83 - 0
taipy/gui/servers/server.py

@@ -0,0 +1,83 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import os
+import re
+import typing as t
+from abc import ABC, abstractmethod
+from contextvars import ContextVar
+
+from ..utils._css import get_style
+
+
+class _Server(ABC):
+    _RE_OPENING_CURLY = re.compile(r"([^\"])(\{)")
+    _RE_CLOSING_CURLY = re.compile(r"(\})([^\"])")
+    _OPENING_CURLY = r"\1&#x7B;"
+    _CLOSING_CURLY = r"&#x7D;\2"
+    _RESOURCE_HANDLER_ARG = "tprh"
+
+    @abstractmethod
+    def get_server_instance(self):
+        raise NotImplementedError
+
+    @abstractmethod
+    def get_port(self) -> int:
+        raise NotImplementedError
+
+    @abstractmethod
+    def send_ws_message(self, *args, **kwargs):
+        raise NotImplementedError
+
+    @abstractmethod
+    def direct_render_json(self, data):
+        raise NotImplementedError
+
+    def render(self, html_fragment, script_paths, style, head, context):
+        template_str = _Server._RE_OPENING_CURLY.sub(_Server._OPENING_CURLY, html_fragment)
+        template_str = _Server._RE_CLOSING_CURLY.sub(_Server._CLOSING_CURLY, template_str)
+        template_str = template_str.replace('"{!', "{")
+        template_str = template_str.replace('!}"', "}")
+        style = get_style(style)
+        return self.direct_render_json(
+            {
+                "jsx": template_str,
+                "style": (style + os.linesep) if style else "",
+                "head": head or [],
+                "context": context or self._gui._get_default_module_name(),  # type: ignore[attr-defined]
+                "scriptPaths": script_paths,
+            }
+        )
+
+    @abstractmethod
+    def run(
+        self,
+        host,
+        port,
+        client_url,
+        debug,
+        use_reloader,
+        server_log,
+        run_in_thread,
+        allow_unsafe_werkzeug,
+        notebook_proxy,
+        port_auto_ranges,
+    ):
+        raise NotImplementedError
+
+    @abstractmethod
+    def stop_thread(self):
+        raise NotImplementedError
+
+
+ServerFrameworks = t.Literal["flask", "fastapi"]
+server_type: ContextVar[ServerFrameworks] = ContextVar("server_type", default="flask")
+server: ContextVar[_Server] = ContextVar("server")

+ 90 - 0
taipy/gui/servers/utils.py

@@ -0,0 +1,90 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import typing as t
+
+from flask import g as flask_meta
+from flask import has_app_context
+from flask import has_request_context as flask_has_request_context
+from flask import request as flask_request
+from flask import send_file as flask_send_file
+from flask import send_from_directory as flask_send_from_directory
+
+from .fastapi import FastAPIServer
+from .flask import FlaskServer
+from .request import request, request_meta
+from .server import ServerFrameworks, _Server, server, server_type
+
+
+def set_server_type(framework: ServerFrameworks) -> None:
+    server_type.set(framework)
+
+
+def get_server_type() -> ServerFrameworks:
+    return server_type.get()
+
+
+def create_server(*args, **kwargs) -> _Server:
+    new_server: t.Union[FlaskServer, FastAPIServer, None] = None
+    if server_type.get() == "flask":
+        new_server = FlaskServer(*args, **kwargs)
+    elif server_type.get() == "fastapi":
+        new_server = FastAPIServer(*args, **kwargs)
+    if new_server is None:
+        raise ValueError(f"Invalid server type: {type}")
+    server.set(new_server)
+    return new_server
+
+
+def send_file(*args, **kwargs):
+    if server_type.get() == "flask":
+        return flask_send_file(*args, **kwargs)
+    elif server_type.get() == "fastapi":
+        raise NotImplementedError("send_file is not supported in FastAPI server")
+
+
+def send_from_directory(*args, **kwargs):
+    if server_type.get() == "flask":
+        return flask_send_from_directory(*args, **kwargs)
+    elif server_type.get() == "fastapi":
+        raise NotImplementedError("send_from_directory is not supported in FastAPI server")
+
+
+def get_request():
+    if server_type.get() == "flask":
+        return flask_request
+    elif server_type.get() == "fastapi":
+        return request.get()
+    return None
+
+
+def get_request_meta():
+    if server_type.get() == "flask":
+        return flask_meta
+    elif server_type.get() == "fastapi":
+        return request_meta.get()
+    return {}
+
+
+def has_server_context():
+    if server_type.get() == "flask":
+        return has_app_context()
+    elif server_type.get() == "fastapi":
+        return True
+    return False
+
+
+def has_request_context():
+    if server_type.get() == "flask":
+        return flask_has_request_context()
+    elif server_type.get() == "fastapi":
+        return True
+    return False

+ 6 - 5
taipy/gui/state.py

@@ -17,8 +17,6 @@ from operator import attrgetter
 from pathlib import Path
 from types import FrameType, SimpleNamespace
 
-from flask import has_app_context
-
 from .utils import _get_module_name_from_frame, _is_in_notebook
 from .utils._attributes import _attrsetter
 
@@ -150,8 +148,7 @@ class State(SimpleNamespace, metaclass=ABCMeta):
         self._gui.set_favicon(favicon_path, self)
 
     @abstractmethod
-    def __getitem__(self, key: str) -> "State":
-        ...
+    def __getitem__(self, key: str) -> "State": ...
 
 
 class _GuiState(State):
@@ -255,7 +252,11 @@ class _GuiState(State):
         return nullcontext()
 
     def _notebook_context(self, gui: "Gui"):
-        return gui.get_flask_app().app_context() if not has_app_context() and _is_in_notebook() else nullcontext()
+        from .servers import has_server_context
+
+        return (
+            gui.get_server_instance().app_context() if not has_server_context() and _is_in_notebook() else nullcontext()
+        )
 
     def _get_placeholder(self, name: str):
         if name in _GuiState.__placeholder_attrs:

+ 15 - 9
taipy/gui/utils/_locals_context.py

@@ -14,8 +14,6 @@ from __future__ import annotations
 import contextlib
 import typing as t
 
-from flask import g
-
 
 class _LocalsContext:
     __ctx_g_name = "locals_context"
@@ -51,26 +49,34 @@ class _LocalsContext:
 
     @contextlib.contextmanager
     def set_locals_context(self, context: t.Optional[str]) -> t.Iterator[None]:
+        from ..servers import get_request_meta
+
         has_set_context = False
         try:
             if context in self._locals_map:
-                if hasattr(g, _LocalsContext.__ctx_g_name):
-                    self._lc_stack.append(getattr(g, _LocalsContext.__ctx_g_name))
-                setattr(g, _LocalsContext.__ctx_g_name, context)
+                if hasattr(get_request_meta(), _LocalsContext.__ctx_g_name):
+                    self._lc_stack.append(getattr(get_request_meta(), _LocalsContext.__ctx_g_name))
+                setattr(get_request_meta(), _LocalsContext.__ctx_g_name, context)
                 has_set_context = True
             yield
         finally:
-            if has_set_context and hasattr(g, _LocalsContext.__ctx_g_name):
+            if has_set_context and hasattr(get_request_meta(), _LocalsContext.__ctx_g_name):
                 if len(self._lc_stack) > 0:
-                    setattr(g, _LocalsContext.__ctx_g_name, self._lc_stack.pop())
+                    setattr(get_request_meta(), _LocalsContext.__ctx_g_name, self._lc_stack.pop())
                 else:
-                    delattr(g, _LocalsContext.__ctx_g_name)
+                    delattr(get_request_meta(), _LocalsContext.__ctx_g_name)
 
     def get_locals(self) -> t.Dict[str, t.Any]:
         return self.get_default() if (context := self.get_context()) is None else self._locals_map[context]
 
     def get_context(self) -> t.Optional[str]:
-        return getattr(g, _LocalsContext.__ctx_g_name) if hasattr(g, _LocalsContext.__ctx_g_name) else None
+        from ..servers import get_request_meta
+
+        return (
+            getattr(get_request_meta(), _LocalsContext.__ctx_g_name)
+            if hasattr(get_request_meta(), _LocalsContext.__ctx_g_name)
+            else None
+        )
 
     def is_default(self) -> bool:
         return self.get_default() == self.get_locals()

+ 1 - 1
taipy/gui/utils/proxy.py

@@ -68,7 +68,7 @@ class _TaipyReverseProxyResource(Resource):
         )
 
     def _get_port(self):
-        return self._gui._server._port
+        return self._gui._server.get_port()
 
     def render(self, request):
         port = self._get_port()

+ 5 - 5
tests/gui/actions/test_download.py

@@ -29,10 +29,10 @@ def test_download(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(t.cast(Flask, gui._server.get_flask()))
+    ws_client = gui._server._ws.test_client(t.cast(Flask, gui._server.get_server_instance()))
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         download(gui._Gui__state, "some text", "filename.txt", "on_download_action")  # type: ignore[attr-defined]
 
@@ -53,10 +53,10 @@ def test_download_fn(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(t.cast(Flask, gui._server.get_flask()))
+    ws_client = gui._server._ws.test_client(t.cast(Flask, gui._server.get_server_instance()))
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         download(gui._Gui__state, "some text", "filename.txt", on_download_action)  # type: ignore[attr-defined]
 
@@ -66,7 +66,7 @@ def test_download_fn(gui: Gui, helpers):
         "DF",
         {"name": "filename.txt", "context": "test_download"},
     )
-    assert "onAction" in received_messages[0]["args"] # inner function is treated as lambda
+    assert "onAction" in received_messages[0]["args"]  # inner function is treated as lambda
 
 
 def test_bad_download(gui: Gui, helpers):

+ 2 - 2
tests/gui/actions/test_get_module_context.py

@@ -26,7 +26,7 @@ def test_get_module_context(gui: Gui, helpers):
     flask_client = gui._server.test_client()
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         g.client_id = cid
         module = get_module_context(gui._Gui__state)  # type: ignore[attr-defined]
         assert module == "test_get_module_context"
@@ -47,7 +47,7 @@ def test_get_module_name_from_state(gui: Gui, helpers):
     flask_client = gui._server.test_client()
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         g.client_id = cid
         module = get_module_name_from_state(gui._Gui__state)  # type: ignore[attr-defined]
         assert module == "test_get_module_context"

+ 1 - 1
tests/gui/actions/test_get_state_id.py

@@ -26,7 +26,7 @@ def test_get_state_id(gui: Gui, helpers):
     flask_client = gui._server.test_client()
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         g.client_id = cid
         assert cid == get_state_id(gui._Gui__state)  # type: ignore[attr-defined]
 

+ 1 - 1
tests/gui/actions/test_get_user_content_url.py

@@ -27,7 +27,7 @@ def test_get_content_url(gui: Gui, helpers):
     # WS client and emit
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         url = get_user_content_url(gui._Gui__state, "path")  # type: ignore[attr-defined]
         assert url == "/taipy-user-content/path?client_id=test"

+ 2 - 2
tests/gui/actions/test_hold_control.py

@@ -25,10 +25,10 @@ def test_hold_control(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())  # type: ignore[arg-type]
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())  # type: ignore[arg-type]
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         hold_control(gui._Gui__state)  # type: ignore[attr-defined]
 

+ 2 - 2
tests/gui/actions/test_invoke_callback.py

@@ -19,7 +19,7 @@ from taipy.gui import Gui, Markdown, State
 
 @contextlib.contextmanager
 def get_state(gui: Gui, state_id: str):
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         client_id = gui._bindings()._get_or_create_scope(state_id)[0]
         gui._Gui__set_client_id_in_context(client_id)  # type: ignore[attr-defined]
         yield gui._Gui__state  # type: ignore[attr-defined]
@@ -65,7 +65,7 @@ def test_invoke_callback_sid(gui: Gui, helpers):
 
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         g.client_id = base_sid
         gui.invoke_callback(cid, user_callback, [])
         assert g.client_id == base_sid

+ 2 - 2
tests/gui/actions/test_navigate.py

@@ -25,10 +25,10 @@ def test_navigate(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())  # type: ignore[arg-type]
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())  # type: ignore[arg-type]
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         navigate(gui._Gui__state, "test")  # type: ignore[attr-defined]
 

+ 4 - 4
tests/gui/actions/test_notify.py

@@ -25,10 +25,10 @@ def test_notify(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())  # type: ignore[arg-type]
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())  # type: ignore[arg-type]
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         id = notify(gui._Gui__state, "Info", "Message", id="id")  # type: ignore[attr-defined]
         assert id == "id"
@@ -53,11 +53,11 @@ def test_close_notification(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())  # type: ignore[arg-type]
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())  # type: ignore[arg-type]
     cid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         id = notify(gui._Gui__state, "Info", "Message", id="id")  # type: ignore[attr-defined]
         close_notification(gui._Gui__state, id)  # type: ignore[attr-defined, arg-type]

+ 2 - 2
tests/gui/actions/test_resume_control.py

@@ -25,10 +25,10 @@ def test_resume_control(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())  # type: ignore[arg-type]
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())  # type: ignore[arg-type]
     cid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         resume_control(gui._Gui__state)  # type: ignore[attr-defined]
 

+ 1 - 1
tests/gui/config/test_cli.py

@@ -76,7 +76,7 @@ def test_gui_service_arguments_hierarchy():
     assert not service_config["debug"]
     assert not service_config["extended_status"]
     assert service_config["favicon"] is None
-    assert not service_config["flask_log"]
+    assert not service_config["server_log"]
     assert service_config["host"] == "127.0.0.1"
     assert service_config["light_theme"] is None
     assert service_config["margin"] is None

+ 34 - 33
tests/gui/data/test_pandas_data_accessor.py

@@ -32,21 +32,24 @@ class MockDataFormat:
     LIST = Mock(value="list")
     CSV = Mock(value="csv")
 
+
 @pytest.fixture
 def pandas_accessor():
     gui = Mock()
     return _PandasDataAccessor(gui=gui)
 
+
 @pytest.fixture
 def sample_df():
     data = {
         "StringCol": ["Apple", "Banana", "Cherry", "apple"],
         "NumberCol": [10, 20, 30, 40],
         "BoolCol": [True, False, True, False],
-        "DateCol": pandas.to_datetime(["2020-01-01", "2021-06-15", "2022-08-22", "2023-03-05"])
+        "DateCol": pandas.to_datetime(["2020-01-01", "2021-06-15", "2022-08-22", "2023-03-05"]),
     }
     return pandas.DataFrame(data)
 
+
 def test_simple_data(gui: Gui, helpers, small_dataframe):
     accessor = _PandasDataAccessor(gui)
     pd = pandas.DataFrame(data=small_dataframe)
@@ -101,7 +104,7 @@ def test_style(gui: Gui, helpers, small_dataframe):
     pd = pandas.DataFrame(data=small_dataframe)
     gui.run(run_server=False)
     cid = helpers.create_scope_and_get_sid(gui)
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
         value = accessor.get_data("x", pd, {"start": 0, "end": 1, "styles": {"st": "test_style"}}, _DataFormat.JSON)[
             "value"
@@ -120,7 +123,7 @@ def test_tooltip(gui: Gui, helpers, small_dataframe):
     pd = pandas.DataFrame(data=small_dataframe)
     gui.run(run_server=False)
     cid = helpers.create_scope_and_get_sid(gui)
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         gui._bind_var_val("tt", tt)
         gui._get_locals_bind_from_context(None)["tt"] = tt
         g.client_id = cid
@@ -139,7 +142,7 @@ def test_format_fn(gui: Gui, helpers, small_dataframe):
     pd = pandas.DataFrame(data=small_dataframe)
     gui.run(run_server=False)
     cid = helpers.create_scope_and_get_sid(gui)
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         gui._bind_var_val("ff", ff)
         gui._get_locals_bind_from_context(None)["ff"] = ff
         g.client_id = cid
@@ -279,58 +282,54 @@ def test_filter_by_date(gui: Gui, helpers, small_dataframe):
     value = accessor.get_data("x", pd, query, _DataFormat.JSON)
     assert len(value["value"]["data"]) == 1
 
+
 def test_contains_case_sensitive(pandas_accessor, sample_df):
-    payload = {
-        "filters": [{"col": "StringCol", "value": "Apple", "action": "contains", "matchCase": True}]
-    }
+    payload = {"filters": [{"col": "StringCol", "value": "Apple", "action": "contains", "matchCase": True}]}
     result = pandas_accessor.get_data("test_var", sample_df, payload, MockDataFormat.LIST)
-    filtered_data = pandas.DataFrame(result["value"]['data'])
+    filtered_data = pandas.DataFrame(result["value"]["data"])
 
     assert len(filtered_data) == 1
-    assert filtered_data.iloc[0]['StringCol'] == 'Apple'
+    assert filtered_data.iloc[0]["StringCol"] == "Apple"
+
 
 def test_contains_case_insensitive(pandas_accessor, sample_df):
-    payload = {
-        "filters": [{"col": "StringCol", "value": "apple", "action": "contains", "matchCase": False}]
-    }
+    payload = {"filters": [{"col": "StringCol", "value": "apple", "action": "contains", "matchCase": False}]}
     result = pandas_accessor.get_data("test_var", sample_df, payload, MockDataFormat.LIST)
-    filtered_data = pandas.DataFrame(result["value"]['data'])
+    filtered_data = pandas.DataFrame(result["value"]["data"])
 
     assert len(filtered_data) == 2
-    assert 'Apple' in filtered_data['StringCol'].values
-    assert 'apple' in filtered_data['StringCol'].values
+    assert "Apple" in filtered_data["StringCol"].values
+    assert "apple" in filtered_data["StringCol"].values
+
 
 def test_equals_case_sensitive(pandas_accessor, sample_df):
-    payload = {
-        "filters": [{"col": "StringCol", "value": "Apple", "action": "==", "matchCase": True}]
-    }
+    payload = {"filters": [{"col": "StringCol", "value": "Apple", "action": "==", "matchCase": True}]}
     result = pandas_accessor.get_data("test_var", sample_df, payload, MockDataFormat.LIST)
-    filtered_data = pandas.DataFrame(result["value"]['data'])
+    filtered_data = pandas.DataFrame(result["value"]["data"])
 
     assert len(filtered_data) == 1
-    assert filtered_data.iloc[0]['StringCol'] == 'Apple'
+    assert filtered_data.iloc[0]["StringCol"] == "Apple"
+
 
 def test_equals_case_insensitive(pandas_accessor, sample_df):
-    payload = {
-        "filters": [{"col": "StringCol", "value": "apple", "action": "==", "matchCase": False}]
-    }
+    payload = {"filters": [{"col": "StringCol", "value": "apple", "action": "==", "matchCase": False}]}
     result = pandas_accessor.get_data("test_var", sample_df, payload, MockDataFormat.LIST)
-    filtered_data = pandas.DataFrame(result["value"]['data'])
+    filtered_data = pandas.DataFrame(result["value"]["data"])
 
     assert len(filtered_data) == 2
-    assert 'Apple' in filtered_data['StringCol'].values
-    assert 'apple' in filtered_data['StringCol'].values
+    assert "Apple" in filtered_data["StringCol"].values
+    assert "apple" in filtered_data["StringCol"].values
+
 
 def test_not_equals_case_insensitive(pandas_accessor, sample_df):
-    payload = {
-        "filters": [{"col": "StringCol", "value": "apple", "action": "!=", "matchCase": False}]
-    }
+    payload = {"filters": [{"col": "StringCol", "value": "apple", "action": "!=", "matchCase": False}]}
     result = pandas_accessor.get_data("test_var", sample_df, payload, MockDataFormat.LIST)
-    filtered_data = pandas.DataFrame(result["value"]['data'])
+    filtered_data = pandas.DataFrame(result["value"]["data"])
 
     assert len(filtered_data) == 2
-    assert 'Banana' in filtered_data['StringCol'].values
-    assert 'Cherry' in filtered_data['StringCol'].values
+    assert "Banana" in filtered_data["StringCol"].values
+    assert "Cherry" in filtered_data["StringCol"].values
+
 
 def test_decimator(gui: Gui, helpers, small_dataframe):
     a_decimator = ScatterDecimator(threshold=1)  # noqa: F841
@@ -348,7 +347,7 @@ def test_decimator(gui: Gui, helpers, small_dataframe):
     cid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         g.client_id = cid
 
         ret_data = accessor.get_data(
@@ -431,6 +430,7 @@ def test_csv(gui, small_dataframe):
     assert path is not None
     assert os.path.getsize(path) > 0
 
+
 def test_multi_index(gui):
     pandas_accessor = _PandasDataAccessor(gui)
 
@@ -443,6 +443,7 @@ def test_multi_index(gui):
         assert result.get("error") is None
         assert result["value"] is not None
 
+
 def test_multi_index_columns(gui):
     pandas_accessor = _PandasDataAccessor(gui)
 

+ 5 - 3
tests/gui/gui_specific/test_broadcast.py

@@ -19,11 +19,12 @@ from taipy.gui import Gui
 
 @contextlib.contextmanager
 def get_state(gui: Gui, state_id: str):
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         client_id = gui._bindings()._get_or_create_scope(state_id)[0]
         gui._Gui__set_client_id_in_context(client_id)  # type: ignore[attr-defined]
         yield gui._Gui__state  # type: ignore[attr-defined]
 
+
 def test_multiple_scopes(gui: Gui):
     var = 1  # noqa: F841
     gui._set_frame(inspect.currentframe())
@@ -77,6 +78,7 @@ def test_broadcast_change(gui: Gui):
         assert state2.v1 == "none"
         assert state2.v2 == 2
 
+
 def test_broadcast_changes(gui: Gui):
     # Bind test variables
     v1 = "none"  # noqa: F841
@@ -87,7 +89,7 @@ def test_broadcast_changes(gui: Gui):
     s1, _ = gui._bindings()._get_or_create_scope("s1")
     s2, _ = gui._bindings()._get_or_create_scope("s2")
 
-    changes = { "v1": "some", "v2": 2}
+    changes = {"v1": "some", "v2": 2}
     gui.broadcast_changes(changes)
     with get_state(gui, s1) as state1:
         assert state1.v1 == "some"
@@ -104,7 +106,7 @@ def test_broadcast_changes(gui: Gui):
         assert state2.v1 == "more"
         assert state2.v2 == 3
 
-    gui.broadcast_changes({ "v1": "more yet"}, v2=4)
+    gui.broadcast_changes({"v1": "more yet"}, v2=4)
     with get_state(gui, s1) as state1:
         assert state1.v1 == "more yet"
         assert state1.v2 == 4

+ 1 - 2
tests/gui/gui_specific/test_favicon.py

@@ -16,14 +16,13 @@ from taipy.gui import Gui, Markdown
 
 
 def test_favicon(gui: Gui, helpers):
-
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
         gui.add_page("test", Markdown("#This is a page"))
         gui.run(run_server=False)
         client = gui._server.test_client()
         # WS client and emit
-        ws_client = gui._server._ws.test_client(gui._server.get_flask())
+        ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
         # Get the jsx once so that the page will be evaluated -> variable will be registered
         sid = helpers.create_scope_and_get_sid(gui)
         client.get(f"/taipy-jsx/test/?client_id={sid}")

+ 13 - 10
tests/gui/gui_specific/test_gui.py

@@ -29,7 +29,7 @@ def test__get_real_var_name(gui: Gui):
     assert res[1] == ""
 
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         with gui._set_locals_context(_get_module_name_from_frame(frame)) if frame else nullcontext():
             with pytest.raises(NameError):
                 res = gui._get_real_var_name(f"{_TaipyContent.get_hash()}_var")
@@ -37,13 +37,13 @@ def test__get_real_var_name(gui: Gui):
 
 def test__get_user_instance(gui: Gui):
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         gui._get_user_instance("", type(None))
 
 
 def test__refresh_expr(gui: Gui):
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         res = gui._refresh_expr("var", None)
         assert res is None
 
@@ -51,7 +51,7 @@ def test__refresh_expr(gui: Gui):
 def test__tbl_cols(gui: Gui):
     data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         res = gui._tbl_cols(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
         assert isinstance(res, str)
 
@@ -66,7 +66,7 @@ def test__tbl_cols(gui: Gui):
 def test__chart_conf(gui: Gui):
     data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         res = gui._chart_conf(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
         assert isinstance(res, str)
 
@@ -84,20 +84,23 @@ def test__chart_conf(gui: Gui):
 
 def test__get_valid_adapter_result(gui: Gui):
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         res = gui._get_valid_adapter_result(("id", "label"))
         assert isinstance(res, tuple)
         assert res[0] == "id"
 
-def test_on_action_call(gui:Gui):
+
+def test_on_action_call(gui: Gui):
     an_id = "my_id"
 
     a_non_action_payload = {"a": "b"}
+
     def on_action(state, id, payload):
         assert id == an_id
         assert payload is a_non_action_payload
 
     an_action_payload = {"action": "on_an_action"}
+
     def on_an_action(state, id, payload):
         assert id == an_id
         assert payload is an_action_payload
@@ -106,6 +109,6 @@ def test_on_action_call(gui:Gui):
     gui._set_frame(inspect.currentframe())
 
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
-        gui._Gui__on_action(an_id, a_non_action_payload) # type: ignore[attr-defined]
-        gui._Gui__on_action(an_id, an_action_payload) # type: ignore[attr-defined]
+    with gui.get_server_instance().app_context():
+        gui._Gui__on_action(an_id, a_non_action_payload)  # type: ignore[attr-defined]
+        gui._Gui__on_action(an_id, an_action_payload)  # type: ignore[attr-defined]

+ 1 - 1
tests/gui/gui_specific/test_locals_context.py

@@ -19,7 +19,7 @@ from taipy.gui.utils._locals_context import _LocalsContext
 def test_locals_context(gui: Gui):
     lc = _LocalsContext()
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         with pytest.raises(KeyError):
             lc.get_default()
         current_locals = locals()

+ 2 - 2
tests/gui/gui_specific/test_navigate.py

@@ -25,7 +25,7 @@ def test_navigate(gui: Gui, helpers):
         gui.run(run_server=False)
         client = gui._server.test_client()
         # WS client and emit
-        ws_client = gui._server._ws.test_client(gui._server.get_flask())
+        ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
         # Get the jsx once so that the page will be evaluated -> variable will be registered
         sid = helpers.create_scope_and_get_sid(gui)
         client.get(f"/taipy-jsx/test/?client_id={sid}")
@@ -44,7 +44,7 @@ def test_navigate_to_no_route(gui: Gui, helpers):
         gui.run(run_server=False)
         client = gui._server.test_client()
         # WS client and emit
-        ws_client = gui._server._ws.test_client(gui._server.get_flask())
+        ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
         # Get the jsx once so that the page will be evaluated -> variable will be registered
         sid = helpers.create_scope_and_get_sid(gui)
         client.get(f"/taipy-jsx/test/?client_id={sid}")

+ 2 - 6
tests/gui/gui_specific/test_state.py

@@ -24,7 +24,7 @@ def test_state(gui: Gui):
     gui.add_page("page1", md_page1)
     gui.run(run_server=False, single_client=True)
     state = gui._Gui__state  # type: ignore[attr-defined]
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         assert state.a == 10
         assert state["page1"].a == 20
         assert state["tests.gui.gui_specific.state_asset.page1"].a == 20
@@ -51,11 +51,7 @@ def test_state(gui: Gui):
 
         assert state._get_placeholder("_taipy_p1") == 10
 
-        assert state._get_placeholder_attrs() == (
-            "_taipy_p1",
-            "_current_context",
-            "__state_id"
-        )
+        assert state._get_placeholder_attrs() == ("_taipy_p1", "_current_context", "__state_id")
 
         assert get_a(state) == 20
 

+ 1 - 1
tests/gui/gui_specific/test_variable_binding.py

@@ -34,7 +34,7 @@ def test_variable_binding(helpers):
     assert gui._bindings().x == x
     assert gui._bindings().y == y
     assert gui._bindings().z == z
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         assert callable(gui._get_user_function("another_function"))
     helpers.test_cleanup()
 

+ 1 - 1
tests/gui/gui_specific/test_variable_directory.py

@@ -19,7 +19,7 @@ from .state_asset.page2 import md_page2
 
 def test_variable_directory_dyanmic_process(gui: Gui):
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         locals_context = _LocalsContext()
         variable_directory = _VariableDirectory(locals_context)
         page1_module = str(md_page1._get_module_name())

+ 1 - 1
tests/gui/helpers.py

@@ -154,7 +154,7 @@ class Helpers:
                 client_url=gui._get_config("client_url", "http://localhost:{port}"),
                 debug=False,
                 use_reloader=False,
-                flask_log=False,
+                server_log=False,
                 run_in_thread=True,
                 allow_unsafe_werkzeug=False,
                 notebook_proxy=False,

+ 1 - 1
tests/gui/long_runnig/test_long_running.py

@@ -35,7 +35,7 @@ def test_long_callback(gui: Gui):
     gui.run(run_server=False, single_client=True)
     state = gui._Gui__state  # type: ignore[attr-defined]
 
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         assert state.status is None
         invoke_long_callback(state, heavy_function)
         invoke_long_callback(state, heavy_function_with_exception)

+ 4 - 4
tests/gui/server/http/test_file_upload.py

@@ -103,7 +103,7 @@ def test_file_upload_multiple(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
     gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         gui._bind_var_val(var_name, None)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = _DataScopes._GLOBAL_ID
@@ -134,13 +134,13 @@ def test_file_upload_folder(gui: Gui, helpers):
 
     sid = _DataScopes._GLOBAL_ID
     files = [(io.BytesIO(b"(^~^)"), "cutey.txt"), (io.BytesIO(b"(^~^)"), "cute_nested.txt")]
-    folders = [ ["folder"], ["folder", "nested"] ]
+    folders = [["folder"], ["folder", "nested"]]
     for file, folder in zip(files, folders):
         path = os.path.join(*folder, file[1])
         response = flask_client.post(
             f"/taipy-uploads?client_id={sid}",
             data={"var_name": "cute_varname", "blob": file, "path": path},
-            content_type="multipart/form-data"
+            content_type="multipart/form-data",
         )
         assert response.status_code == 200
-        assert os.path.isfile( os.path.join( gui._get_config("upload_folder", tempfile.gettempdir()), path) )
+        assert os.path.isfile(os.path.join(gui._get_config("upload_folder", tempfile.gettempdir()), path))

+ 1 - 1
tests/gui/server/ws/test_a.py

@@ -30,7 +30,7 @@ def test_a_button_pressed(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 1 - 1
tests/gui/server/ws/test_broadcast.py

@@ -29,7 +29,7 @@ def test_broadcast(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     sid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 1 - 1
tests/gui/server/ws/test_df.py

@@ -29,7 +29,7 @@ def test_download_file(gui: Gui, helpers):
 
     gui.run(run_server=False)
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
     ws_client.emit("message", {"client_id": sid, "type": "A", "name": "my_button", "payload": "do_something"})

+ 1 - 1
tests/gui/server/ws/test_du.py

@@ -32,7 +32,7 @@ def test_du_table_data_fetched(gui: Gui, helpers, csvdata):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     sid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 2 - 2
tests/gui/server/ws/test_on_change.py

@@ -29,7 +29,7 @@ def test_default_on_change(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")
@@ -57,7 +57,7 @@ def test_specific_on_change(gui: Gui, helpers):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 1 - 1
tests/gui/server/ws/test_ru.py

@@ -29,7 +29,7 @@ def test_ru_selector(gui: Gui, helpers, csvdata):
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     sid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 1 - 1
tests/gui/server/ws/test_u.py

@@ -26,7 +26,7 @@ def ws_u_assert_template(gui: Gui, helpers, value_before_update, value_after_upd
     gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
     flask_client.get(f"/taipy-jsx/test?client_id={sid}")

+ 2 - 2
tests/gui/server/ws/test_with.py

@@ -26,14 +26,14 @@ def test_sending_messages_in_group(gui: Gui, helpers):
     gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     # WS client and emit
-    ws_client = gui._server._ws.test_client(gui._server.get_flask())
+    ws_client = gui._server._ws.test_client(gui._server.get_server_instance())
     cid = _DataScopes._GLOBAL_ID
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     flask_client.get(f"/taipy-jsx/test?client_id={cid}")
     assert gui._bindings()._get_all_scopes()[cid].name == "World!"  # type: ignore
     assert gui._bindings()._get_all_scopes()[cid].btn_id == "button1"  # type: ignore
 
-    with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
+    with gui.get_server_instance().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
         with gui as aGui:
             aGui._Gui__state.name = "Monde!"
             aGui._Gui__state.btn_id = "button2"

+ 6 - 6
tests/gui/utils/test_evaluator.py

@@ -21,7 +21,7 @@ from taipy.gui.utils.types import _TaipyNumber
 def test_unbind_variable_in_expression(gui: Gui, helpers):
     gui.run(run_server=False, single_client=True)
     with warnings.catch_warnings(record=True) as records:
-        with gui.get_flask_app().app_context():
+        with gui.get_server_instance().app_context():
             gui._evaluate_expr("{x}")
             warns = helpers.get_taipy_warnings(records)
             assert len(warns) == 3
@@ -35,7 +35,7 @@ def test_evaluate_same_expression_multiple_times(gui: Gui):
     x = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
     gui.run(run_server=False, single_client=True)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         s1 = gui._evaluate_expr("x + 10 = {x + 10}")
         s2 = gui._evaluate_expr("x + 10 = {x + 10}")
         assert s1 == s2
@@ -45,7 +45,7 @@ def test_evaluate_expressions_same_variable(gui: Gui):
     x = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
     gui.run(run_server=False, single_client=True)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         s1 = gui._evaluate_expr("x + 10 = {x + 10}")
         s2 = gui._evaluate_expr("x = {x}")
         assert "tp_TpExPr_x" in s1 and "tp_TpExPr_x" in s2
@@ -56,7 +56,7 @@ def test_evaluate_holder(gui: Gui):
     gui._set_frame(inspect.currentframe())
     gui.run(run_server=False, single_client=True)
     with warnings.catch_warnings(record=True):
-        with gui.get_flask_app().app_context():
+        with gui.get_server_instance().app_context():
             gui._evaluate_expr("{x + 10}")
             hash = gui._evaluate_bind_holder(_TaipyNumber, "TpExPr_x + 10_TPMDL_0")
             assert "_TpN_tp_TpExPr_x_10_TPMDL_0_0" in hash
@@ -70,7 +70,7 @@ def test_evaluate_holder(gui: Gui):
 
 def test_evaluate_not_expression_type(gui: Gui):
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         assert "x + 10" == gui._evaluate_expr("x + 10")
 
 
@@ -79,7 +79,7 @@ def test_evaluate_expression_2_clients(gui: Gui):
     y = 20  # noqa: F841
     gui._set_frame(inspect.currentframe())
     gui.run(run_server=False)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         gui._bindings()._get_or_create_scope("A")
         gui._bindings()._get_or_create_scope("B")
         g.client_id = "A"

+ 1 - 1
tests/gui/utils/test_map_dict.py

@@ -112,7 +112,7 @@ def test_map_dict_set(gui: Gui, test_client):
     gui._set_frame(inspect.currentframe())
 
     gui.run(run_server=False, single_client=True)
-    with gui.get_flask_app().app_context():
+    with gui.get_server_instance().app_context():
         assert isinstance(gui._Gui__state.d, _MapDict)  # type: ignore[attr-defined]
         gui._Gui__state.d = {"b": 2}  # type: ignore[attr-defined]
         assert isinstance(gui._Gui__state.d, _MapDict)  # type: ignore[attr-defined]