from __future__ import annotations    import contextlib  import logging  import os  import pathlib  import re  import sys  import time  import typing as t  import webbrowser  from importlib import util  from random import randint    import __main__  from flask import Blueprint, Flask, json, jsonify, render_template, send_from_directory  from flask_cors import CORS  from flask_socketio import SocketIO  from gitignore_parser import parse_gitignore  from kthread import KThread  from taipy.logger._taipy_logger import _TaipyLogger  from werkzeug.serving import is_running_from_reloader    from ._renderers.json import _TaipyJsonProvider  from .config import ServerConfig  from .utils import _is_in_notebook, _is_port_open, _RuntimeManager    if t.TYPE_CHECKING:      from .gui import Gui      class _Server:      __RE_OPENING_CURLY = re.compile(r"([^\"])(\{)")      __RE_CLOSING_CURLY = re.compile(r"(\})([^\"])")      __OPENING_CURLY = r"\1{"      __CLOSING_CURLY = r"}\2"        def __init__(          self,          gui: Gui,          flask: t.Optional[Flask] = None,          path_mapping: t.Optional[dict] = None,          async_mode: t.Optional[str] = None,          allow_upgrades: bool = True,          server_config: t.Optional[ServerConfig] = None,      ):          self._gui = gui          server_config = server_config or {}          self._flask = flask          if self._flask is None:              flask_config: t.Dict[str, t.Any] = {"import_name": "Taipy"}              if "flask" in server_config and isinstance(server_config["flask"], dict):                  flask_config.update(server_config["flask"])              self._flask = Flask(**flask_config)          if "SECRET_KEY" not in self._flask.config or not self._flask.config["SECRET_KEY"]:              self._flask.config["SECRET_KEY"] = "TaIpY"            # setup cors          if "cors" not in server_config or (              "cors" in server_config and (isinstance(server_config["cors"], dict) or server_config["cors"] is True)          ):              cors_config = (                  server_config["cors"] if "cors" in server_config and isinstance(server_config["cors"], dict) else {}              )              CORS(self._flask, **cors_config)            # setup socketio          socketio_config: t.Dict[str, t.Any] = {              "cors_allowed_origins": "*",              "ping_timeout": 10,              "ping_interval": 5,              "json": json,              "async_mode": async_mode,              "allow_upgrades": allow_upgrades,          }          if "socketio" in server_config and isinstance(server_config["socketio"], dict):              socketio_config.update(server_config["socketio"])          self._ws = SocketIO(self._flask, **socketio_config)            self._apply_patch()            # set json encoder (for Taipy specific types)          self._flask.json_provider_class = _TaipyJsonProvider          self._flask.json = self._flask.json_provider_class(self._flask)  # type: ignore            self.__path_mapping = path_mapping or {}          self.__ssl_context = server_config.get("ssl_context", None)          self._is_running = False            # Websocket (handle json message)          # adding args for the one call with a server ack request          @self._ws.on("message")          def handle_message(message, *args) -> None:              if "status" in message:                  _TaipyLogger._get_logger().info(message["status"])              elif "type" in message:                  gui._manage_message(message["type"], message)        def __is_ignored(self, file_path: str) -> bool:          if not hasattr(self, "_ignore_matches"):              __IGNORE_FILE = ".taipyignore"              ignore_file = (                  (pathlib.Path(__main__.__file__).parent / __IGNORE_FILE) if hasattr(__main__, "__file__") else None              )              if not ignore_file or not ignore_file.is_file():                  ignore_file = pathlib.Path(self._gui._root_dir) / __IGNORE_FILE              self._ignore_matches = (                  parse_gitignore(ignore_file) if ignore_file.is_file() and os.access(ignore_file, os.R_OK) else None              )            if callable(self._ignore_matches):              return self._ignore_matches(file_path)          return False        def _get_default_blueprint(          self,          static_folder: str,          template_folder: str,          title: str,          favicon: str,          root_margin: str,          scripts: t.List[str],          styles: t.List[str],          version: str,          client_config: t.Dict[str, t.Any],          watermark: t.Optional[str],          css_vars: str,          base_url: str,      ) -> Blueprint:          taipy_bp = Blueprint("Taipy", __name__, static_folder=static_folder, template_folder=template_folder)          # Serve static react build            @taipy_bp.route("/", defaults={"path": ""})          @taipy_bp.route("/<path:path>")          def my_index(path):              if path == "" or path == "index.html" or "." not in path:                  try:                      return render_template(                          "index.html",                          title=title,                          favicon=favicon,                          root_margin=root_margin,                          watermark=watermark,                          config=client_config,                          scripts=scripts,                          styles=styles,                          version=version,                          css_vars=css_vars,                          base_url=base_url,                      )                  except Exception:  # pragma: no cover                      raise RuntimeError(                          "Something is wrong with the taipy-gui front-end installation. Check that the js bundle has been properly built (is Node.js installed?)."  # noqa: E501                      )                if path == "taipy.status.json":                  return self._direct_render_json(self._gui._serve_status(pathlib.Path(template_folder) / path))              if str(os.path.normpath(file_path := ((base_path := static_folder + os.path.sep) + path))).startswith(                  base_path              ) and os.path.isfile(file_path):                  return send_from_directory(base_path, path)              # use the path mapping to detect and find resources              for k, v in self.__path_mapping.items():                  if (                      path.startswith(f"{k}/")                      and str(                          os.path.normpath(file_path := ((base_path := v + os.path.sep) + path[len(k) + 1 :]))                      ).startswith(base_path)                      and os.path.isfile(file_path)                  ):                      return send_from_directory(base_path, path[len(k) + 1 :])              if (                  hasattr(__main__, "__file__")                  and str(                      os.path.normpath(                          file_path := ((base_path := os.path.dirname(__main__.__file__) + os.path.sep) + path)                      )                  ).startswith(base_path)                  and os.path.isfile(file_path)                  and not self.__is_ignored(file_path)              ):                  return send_from_directory(base_path, path)              if (                  str(os.path.normpath(file_path := (base_path := self._gui._root_dir + os.path.sep) + path)).startswith(                      base_path                  )                  and os.path.isfile(file_path)                  and not self.__is_ignored(file_path)              ):                  return send_from_directory(base_path, path)              return ("", 404)            return taipy_bp        # Update to render as JSX      def _render(self, html_fragment, 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('!}"', "}")          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(),              }          )        def _direct_render_json(self, data):          return jsonify(data)        def get_flask(self):          return self._flask        def test_client(self):          return self._flask.test_client()        def _run_notebook(self):          self._is_running = True          self._ws.run(self._flask, host=self._host, port=self._port, debug=False, use_reloader=False)        def _get_async_mode(self) -> str:          return self._ws.async_mode        def _apply_patch(self):          if self._get_async_mode() == "gevent" and util.find_spec("gevent"):              from gevent import monkey                if not monkey.is_module_patched("time"):                  monkey.patch_time()          if self._get_async_mode() == "eventlet" and util.find_spec("eventlet"):              from eventlet import monkey_patch, patcher                if not patcher.is_monkey_patched("time"):                  monkey_patch(time=True)        def _get_random_port(self):  # pragma: no cover          while True:              port = randint(49152, 65535)              if port not in _RuntimeManager().get_used_port() and not _is_port_open(self._host, port):                  return port        def run(self, host, port, debug, use_reloader, flask_log, run_in_thread, allow_unsafe_werkzeug, notebook_proxy):          host_value = host if host != "0.0.0.0" else "localhost"          self._host = host          self._port = port          if _is_in_notebook() and notebook_proxy:  # pragma: no cover              from .utils.proxy import NotebookProxy                # Start proxy if not already started              self._proxy = NotebookProxy(gui=self._gui, listening_port=port)              self._proxy.run()              self._port = self._get_random_port()          if _is_in_notebook() or run_in_thread:              runtime_manager = _RuntimeManager()              runtime_manager.add_gui(self._gui, port)          if debug and not is_running_from_reloader() and _is_port_open(host_value, port):              raise ConnectionError(                  f"Port {port} is already opened on {host_value}. You have another server application running on the same port."  # noqa: E501              )          if not flask_log:              log = logging.getLogger("werkzeug")              log.disabled = True              if not is_running_from_reloader():                  _TaipyLogger._get_logger().info(f" * Server starting on http://{host_value}:{port}")              else:                  _TaipyLogger._get_logger().info(f" * Server reloaded on http://{host_value}:{port}")          if not is_running_from_reloader() and self._gui._get_config("run_browser", False):              webbrowser.open(f"http://{host_value}{f':{port}' if port else ''}", new=2)          if _is_in_notebook() or run_in_thread:              self._thread = KThread(target=self._run_notebook)              self._thread.start()              return          self._is_running = True          run_config = {              "app": self._flask,              "host": host,              "port": port,              "debug": debug,              "use_reloader": use_reloader,          }          if self.__ssl_context is not None:              run_config["ssl_context"] = self.__ssl_context          # flask-socketio specific conditions for 'allow_unsafe_werkzeug' parameters to be popped out of kwargs          if self._get_async_mode() == "threading" and (not sys.stdin or not sys.stdin.isatty()):              run_config = {**run_config, "allow_unsafe_werkzeug": allow_unsafe_werkzeug}          self._ws.run(**run_config)        def stop_thread(self):          if hasattr(self, "_thread") and self._thread.is_alive() and self._is_running:              self._is_running = False              with contextlib.suppress(Exception):                  if self._get_async_mode() == "gevent":                      if self._ws.wsgi_server is not None:                          self._ws.wsgi_server.stop()                      else:                          self._thread.kill()                  else:                      self._thread.kill()              while _is_port_open(self._host, self._port):                  time.sleep(0.1)        def stop_proxy(self):          if hasattr(self, "_proxy"):              self._proxy.stop()


import os  import re  import typing as t  from importlib.util import find_spec    import pytz  import tzlocal  from dotenv import dotenv_values  from taipy.logger._taipy_logger import _TaipyLogger  from werkzeug.serving import is_running_from_reloader    from ._gui_cli import _GuiCLI  from ._page import _Page  from ._warnings import _warn  from .partial import Partial  from .utils import _is_in_notebook    ConfigParameter = t.Literal[      "allow_unsafe_werkzeug",      "async_mode",      "change_delay",      "chart_dark_template",      "base_url",      "dark_mode",      "dark_theme",      "data_url_max_size",      "debug",      "extended_status",      "favicon",      "flask_log",      "host",      "light_theme",      "margin",      "ngrok_token",      "notebook_proxy",      "notification_duration",      "propagate",      "run_browser",      "run_in_thread",      "run_server",      "server_config",      "single_client",      "system_notification",      "theme",      "time_zone",      "title",      "stylekit",      "upload_folder",      "use_arrow",      "use_reloader",      "watermark",      "webapp_path",      "port",  ]    Stylekit = t.TypedDict(      "Stylekit",      {          "color_primary": str,          "color_secondary": str,          "color_error": str,          "color_warning": str,          "color_success": str,          "color_background_light": str,          "color_paper_light": str,          "color_background_dark": str,          "color_paper_dark": str,          "font_family": str,          "root_margin": str,          "border_radius": int,          "input_button_height": str,      },      total=False,  )    ServerConfig = t.TypedDict(      "ServerConfig",      {          "cors": t.Optional[t.Union[bool, t.Dict[str, t.Any]]],          "socketio": t.Optional[t.Dict[str, t.Any]],          "ssl_context": t.Optional[t.Union[str, t.Tuple[str, str]]],          "flask": t.Optional[t.Dict[str, t.Any]],      },      total=False,  )    Config = t.TypedDict(      "Config",      {          "allow_unsafe_werkzeug": bool,          "async_mode": str,          "change_delay": t.Optional[int],          "chart_dark_template": t.Optional[t.Dict[str, t.Any]],          "base_url": t.Optional[str],          "dark_mode": bool,          "dark_theme": t.Optional[t.Dict[str, t.Any]],          "data_url_max_size": t.Optional[int],          "debug": bool,          "extended_status": bool,          "favicon": t.Optional[str],          "flask_log": bool,          "host": str,          "light_theme": t.Optional[t.Dict[str, t.Any]],          "margin": t.Optional[str],          "ngrok_token": str,          "notebook_proxy": bool,          "notification_duration": int,          "propagate": bool,          "run_browser": bool,          "run_in_thread": bool,          "run_server": bool,          "server_config": t.Optional[ServerConfig],          "single_client": bool,          "system_notification": bool,          "theme": t.Optional[t.Dict[str, t.Any]],          "time_zone": t.Optional[str],          "title": t.Optional[str],          "stylekit": t.Union[bool, Stylekit],          "upload_folder": t.Optional[str],          "use_arrow": bool,          "use_reloader": bool,          "watermark": t.Optional[str],          "webapp_path": t.Optional[str],          "port": int,      },      total=False,  )      class _Config(object):      __RE_PORT_NUMBER = re.compile(          r"^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"      )        def __init__(self):          self.pages: t.List[_Page] = []          self.root_page: t.Optional[_Page] = None          self.routes: t.List[str] = []          self.partials: t.List[Partial] = []          self.partial_routes: t.List[str] = []          self.config: Config = {}        def _load(self, config: Config) -> None:          self.config.update(config)          # Check that the user timezone configuration setting is valid          self.get_time_zone()        def _get_config(self, name: ConfigParameter, default_value: t.Any) -> t.Any:  # pragma: no cover          if name in self.config and self.config[name] is not None:              if default_value is not None and not isinstance(self.config[name], type(default_value)):                  try:                      return type(default_value)(self.config[name])                  except Exception as e:                      _warn(f'app_config "{name}" value "{self.config[name]}" is not of type {type(default_value)}', e)                      return default_value              return self.config[name]          return default_value        def get_time_zone(self) -> t.Optional[str]:          tz = self.config.get("time_zone")          if tz is None or tz == "client":              return tz          if tz == "server":              # return python tzlocal IANA Time Zone              return str(tzlocal.get_localzone())          # Verify user defined IANA Time Zone is valid          if tz not in pytz.all_timezones_set:              raise Exception(                  "Time Zone configuration is not valid. Mistyped 'server', 'client' options or invalid IANA Time Zone"              )          # return user defined IANA Time Zone (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)          return tz        def _handle_argparse(self):          _GuiCLI.create_parser()          args = _GuiCLI.parse_arguments()            config = self.config          if args.taipy_port:              if not _Config.__RE_PORT_NUMBER.match(args.taipy_port):                  _warn("Port value for --port option is not valid.")              else:                  config["port"] = int(args.taipy_port)          if args.taipy_host:              config["host"] = args.taipy_host          if args.taipy_debug:              config["debug"] = True          if args.taipy_no_debug:              config["debug"] = False          if args.taipy_use_reloader:              config["use_reloader"] = True          if args.taipy_no_reloader:              config["use_reloader"] = False          if args.taipy_ngrok_token:              config["ngrok_token"] = args.taipy_ngrok_token          if args.taipy_webapp_path:              config["webapp_path"] = args.taipy_webapp_path          elif os.environ.get("TAIPY_GUI_WEBAPP_PATH"):              config["webapp_path"] = os.environ.get("TAIPY_GUI_WEBAPP_PATH")        def _build_config(self, root_dir, env_filename, kwargs):  # pragma: no cover          config = self.config          env_file_abs_path = env_filename if os.path.isabs(env_filename) else os.path.join(root_dir, env_filename)          # Load keyword arguments          for key, value in kwargs.items():              key = key.lower()              if value is not None and key in config:                  # Special case for "stylekit" that can be a Boolean or a dict                  if key == "stylekit" and isinstance(value, bool):                      from ._default_config import _default_stylekit                        config[key] = _default_stylekit if value else {}                      continue                  try:                      if isinstance(value, dict) and isinstance(config[key], dict):                          config[key].update(value)                      else:                          config[key] = value if config[key] is None else type(config[key])(value)  # type: ignore                  except Exception as e:                      _warn(                          f"Invalid keyword arguments value in Gui.run {key} - {value}. Unable to parse value to the correct type",  # noqa: E501                          e,                      )          # Load config from env file          if os.path.isfile(env_file_abs_path):              for key, value in dotenv_values(env_file_abs_path).items():                  key = key.lower()                  if value is not None and key in config:                      try:                          config[key] = value if config[key] is None else type(config[key])(value)  # type: ignore                      except Exception as e:                          _warn(                              f"Invalid env value in Gui.run(): {key} - {value}. Unable to parse value to the correct type",  # noqa: E501                              e,                          )            # Taipy-config          if find_spec("taipy") and find_spec("taipy.config"):              from taipy.config import Config as TaipyConfig                try:                  section = TaipyConfig.unique_sections["gui"]                  self.config.update(section._to_dict())              except KeyError:                  _warn("taipy-config section for taipy-gui is not initialized.")            # Load from system arguments          self._handle_argparse()        def __log_outside_reloader(self, logger, msg):          if not is_running_from_reloader():              logger.info(msg)        def resolve(self):          app_config = self.config          logger = _TaipyLogger._get_logger()          # Special config for notebook runtime          if _is_in_notebook() or app_config["run_in_thread"] and not app_config["single_client"]:              app_config["single_client"] = True              self.__log_outside_reloader(logger, "Running in 'single_client' mode in notebook environment")            if app_config["run_server"] and app_config["ngrok_token"] and app_config["use_reloader"]:              app_config["use_reloader"] = False              self.__log_outside_reloader(                  logger, "'use_reloader' parameter will not be used when 'ngrok_token' parameter is available"              )            if app_config["use_reloader"] and _is_in_notebook():              app_config["use_reloader"] = False              self.__log_outside_reloader(logger, "'use_reloader' parameter is not available in notebook environment")            if app_config["use_reloader"] and not app_config["debug"]:              app_config["debug"] = True              self.__log_outside_reloader(logger, "application is running in 'debug' mode")            if app_config["debug"] and not app_config["allow_unsafe_werkzeug"]:              app_config["allow_unsafe_werkzeug"] = True              self.__log_outside_reloader(logger, "'allow_unsafe_werkzeug' has been set to True")            if app_config["debug"] and app_config["async_mode"] != "threading":              app_config["async_mode"] = "threading"              self.__log_outside_reloader(                  logger,                  "'async_mode' parameter has been overridden to 'threading'. Using Flask built-in development server with debug mode",  # noqa: E501              )            self._resolve_notebook_proxy()          self._resolve_stylekit()          self._resolve_url_prefix()        def _resolve_stylekit(self):          app_config = self.config          # support legacy margin variable          stylekit_config = app_config["stylekit"]            if isinstance(app_config["stylekit"], dict) and "root_margin" in app_config["stylekit"]:              from ._default_config import _default_stylekit, default_config                stylekit_config = app_config["stylekit"]              if (                  stylekit_config["root_margin"] == _default_stylekit["root_margin"]                  and app_config["margin"] != default_config["margin"]              ):                  app_config["stylekit"]["root_margin"] = str(app_config["margin"])              app_config["margin"] = None        def _resolve_url_prefix(self):          app_config = self.config          base_url = app_config.get("base_url")          if base_url is None:              app_config["base_url"] = "/"          else:              base_url = f"{'' if base_url.startswith('/') else '/'}{base_url}"              base_url = f"{base_url}{'' if base_url.endswith('/') else '/'}"              app_config["base_url"] = base_url        def _resolve_notebook_proxy(self):          app_config = self.config          app_config["notebook_proxy"] = app_config["notebook_proxy"] if _is_in_notebook() else False


from .gui import Gui


import typing as t  from enum import Enum  from inspect import isclass    from .data import Decimator  from .utils import (      _TaipyBase,      _TaipyBool,      _TaipyContent,      _TaipyContentHtml,      _TaipyContentImage,      _TaipyData,      _TaipyDate,      _TaipyDateRange,      _TaipyDict,      _TaipyLoNumbers,      _TaipyLov,      _TaipyLovValue,      _TaipyNumber,  )      class _WsType(Enum):      ACTION = "A"      MULTIPLE_UPDATE = "MU"      REQUEST_UPDATE = "RU"      DATA_UPDATE = "DU"      UPDATE = "U"      ALERT = "AL"      BLOCK = "BL"      NAVIGATE = "NA"      CLIENT_ID = "ID"      MULTIPLE_MESSAGE = "MS"      DOWNLOAD_FILE = "DF"      PARTIAL = "PR"      ACKNOWLEDGEMENT = "ACK"      NumberTypes = {"int", "int64", "float", "float64"}      class PropertyType(Enum):      """      All the possible element property types.        This is used when creating custom visual elements, where you have      to indicate the type of each property.        Some types are 'dynamic', meaning that if the property value is modified, it      is automatically handled by Taipy and propagated to the entire application.        See `ElementProperty^` for more details.      """        boolean = "boolean"      """      The property holds a Boolean value.      """      toHtmlContent = _TaipyContentHtml      content = _TaipyContent      data = _TaipyData      date = _TaipyDate      date_range = _TaipyDateRange      dict = "dict"      """      The property holds a dictionary.      """      dynamic_dict = _TaipyDict      """      The property holds a dynamic dictionary.      """      dynamic_number = _TaipyNumber      """      The property holds a dynamic number.      """      dynamic_lo_numbers = _TaipyLoNumbers      """      The property holds a dynamic list of numbers.      """      dynamic_boolean = _TaipyBool      """      The property holds a dynamic Boolean value.      """      dynamic_list = "dynamiclist"      dynamic_string = "dynamicstring"      """      The property holds a dynamic string.      """      function = "function"      """      The property holds a reference to a function.      """      image = _TaipyContentImage      json = "json"      lov = _TaipyLov      """      The property holds a LoV (list of values).      """      lov_value = _TaipyLovValue      """      The property holds a value in a LoV (list of values).      """      number = "number"      """      The property holds a number.      """      react = "react"      broadcast = "broadcast"      string = "string"      """      The property holds a string.      """      string_or_number = "string|number"      """      The property holds a string or a number.        This is typically used to handle CSS dimension values, where a unit can be used.      """      boolean_or_list = "boolean|list"      slider_value = "number|number[]|lovValue"      string_list = "stringlist"      decimator = Decimator      """      The property holds an inner attributes that is defined by a library and cannot be overridden by the user.      """      inner = "inner"      @t.overload  # noqa: F811  def _get_taipy_type(a_type: None) -> None:      ...      @t.overload  def _get_taipy_type(a_type: t.Type[_TaipyBase]) -> t.Type[_TaipyBase]:  # noqa: F811      ...      @t.overload  def _get_taipy_type(a_type: PropertyType) -> t.Type[_TaipyBase]:  # noqa: F811      ...      @t.overload  def _get_taipy_type(  # noqa: F811      a_type: t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]  ) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]:      ...      def _get_taipy_type(  # noqa: F811      a_type: t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]  ) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]:      if a_type is None:          return None      if isinstance(a_type, PropertyType) and not isinstance(a_type.value, str):          return a_type.value      if isclass(a_type) and not isinstance(a_type, PropertyType) and issubclass(a_type, _TaipyBase):          return a_type      if a_type == PropertyType.boolean:          return _TaipyBool      elif a_type == PropertyType.number:          return _TaipyNumber      return None


"""# Taipy Graphical User Interface generator    The Taipy GUI package provides User Interface generation based on page templates. It can  run a web server that a web browser can connect to.    The pages are generated by a web server that allows web clients to connect, display and  interact with the page content through visual elements.    Each page can contain regular text and images, as well as Taipy controls that are  typically linked to some value that is managed by the whole Taipy application.    Here is how you can create your first Taipy User Interface:       - Create a Python source file.       Copy these two lines into a file called *taipy_app.py*.       ```py title="taipy_app.py"       from taipy import Gui       Gui("# Hello Taipy!").run()       ```     - Install Taipy:       ```       pip install taipy       ```     - Run your application:       ```       python taipy_app.py       ```    Your browser opens a new page, showing the content of your graphical  application.    !!! Note "Optional packages"        There are Python packages that you can install in your environment to      add functionality to Taipy GUI:        - [`python-magic`](https://pypi.org/project/python-magic/): identifies image format        from byte buffers so the [`image`](../../gui/viselements/image.md) control can        display them, and so that [`file_download`](../../gui/viselements/file_download.md)        can request the browser to display the image content when relevant.
        You can install that package with the regular `pip install python-magic` command        (then potentially `pip install python-magic` on Windows),        or install Taipy GUI using: `pip install taipy-gui[image]`.      - [`pyarrow`](https://pypi.org/project/pyarrow/): can improve the performance of your        application by reducing the volume of data transferred between the web server and the        clients. This is relevant if your application uses large tabular data.
        You can install that package with the regular `pip install pyarrow` command,        or install Taipy GUI using: `pip install taipy-gui[arrow]`.      - [`simple-websocket`](https://pypi.org/project/simple-websocket/): enables the        debugging of the WebSocket part of the server execution.
        You can install that package with the regular `pip install simple-websocket` command,        or install Taipy GUI using: `pip install taipy-gui[simple-websocket]`.      - [`pyngrok`](https://pypi.org/project/pyngrok/): enables use of [Ngrok](https://ngrok.com/)        to access Taipy GUI application from the internet.
        You can install that package with the regular `pip install pyngrok` command,        or install Taipy GUI using: `pip install taipy-gui[pyngrok]`.    """    from importlib.util import find_spec    from ._init import *  from ._renderers import Html, Markdown  from .gui_actions import (      broadcast_callback,      download,      get_module_context,      get_module_name_from_state,      get_state_id,      get_user_content_url,      hold_control,      invoke_callback,      invoke_long_callback,      navigate,      notify,      resume_control,  )  from .icon import Icon  from .page import Page  from .partial import Partial  from .state import State  from .utils import is_debugging    if find_spec("taipy") and find_spec("taipy.config"):      from taipy.config import _inject_section        from ._default_config import default_config      from ._gui_section import _GuiSection        _inject_section(          _GuiSection,          "gui_config",          _GuiSection(property_list=list(default_config)),          [("configure_gui", _GuiSection._configure)],          add_to_unconflicted_sections=True,      )


from __future__ import annotations    import inspect  import typing as t  from types import FrameType    from .utils import _filter_locals, _get_module_name_from_frame    if t.TYPE_CHECKING:      from ._renderers import _Element      class Page:      """Generic page generator.        The `Page` class transforms template text into actual pages that can be displayed      on a web browser.        When a page is requested to be displayed, it is converted into HTML      code that can be sent to the client. All control placeholders are      replaced by their respective graphical component so you can show      your application variables and interact with them.      """        def __init__(self, **kwargs) -> None:          self._class_module_name = ""          self._class_locals: t.Dict[str, t.Any] = {}          self._frame: t.Optional[FrameType] = None          self._renderer: t.Optional[Page] = self.create_page()          if "frame" in kwargs:              self._frame = kwargs.get("frame")          elif self._renderer:              self._frame = self._renderer._frame          elif len(inspect.stack()) < 4:              raise RuntimeError(f"Can't resolve module. Page '{type(self).__name__}' is not registered.")          else:              self._frame = t.cast(FrameType, t.cast(FrameType, inspect.stack()[3].frame))          if self._renderer:              # Extract the page module's attributes and methods              cls = type(self)              cls_locals = dict(vars(self))              funcs = [                  i[0]                  for i in inspect.getmembers(cls)                  if not i[0].startswith("_") and (inspect.ismethod(i[1]) or inspect.isfunction(i[1]))              ]              for f in funcs:                  func = getattr(self, f)                  if hasattr(func, "__func__") and func.__func__ is not None:                      cls_locals[f] = func.__func__              self._class_module_name = cls.__name__              self._class_locals = cls_locals        def create_page(self) -> t.Optional[Page]:          """Create the page content for page modules.            If this page is a page module, this method must be overloaded and return the page content.            This method should never be called directly: only the Taipy GUI internals will.            The default implementation returns None, indicating that this class does not implement          a page module.            Returns:              The page content for this Page subclass, making it a page module.          """          return None        def _get_locals(self) -> t.Optional[t.Dict[str, t.Any]]:          return (              self._class_locals              if self._is_class_module()              else None              if (frame := self._get_frame()) is None              else _filter_locals(frame.f_locals)          )        def _is_class_module(self):          return self._class_module_name != ""        def _get_frame(self):          if not hasattr(self, "_frame"):              raise RuntimeError(f"Page '{type(self).__name__}' was not registered correctly.")          return self._frame        def _get_module_name(self) -> t.Optional[str]:          return (              None              if (frame := self._get_frame()) is None              else f"{_get_module_name_from_frame(frame)}{'.' if self._class_module_name else ''}{self._class_module_name}"  # noqa: E501          )        def _get_content_detail(self, gui) -> str:          return f"in class {type(self).__name__}"        def render(self, gui) -> str:          if self._renderer is not None:              return self._renderer.render(gui)          return "
No renderer found for page
"


import sys  import traceback  import typing as t  import warnings      class TaipyGuiWarning(UserWarning):      """NOT DOCUMENTED        Warning category for Taipy warnings generated in user code.      """        _tp_debug_mode = False        @staticmethod      def set_debug_mode(debug_mode: bool):          TaipyGuiWarning._tp_debug_mode = (              debug_mode if debug_mode else hasattr(sys, "gettrace") and sys.gettrace() is not None          )      def _warn(message: str, e: t.Optional[BaseException] = None):      warnings.warn(          f"{message}:\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}"          if e and TaipyGuiWarning._tp_debug_mode          else f"{message}:\n{e}"          if e          else message,          TaipyGuiWarning,          stacklevel=2,      )


import threading  import typing as t    from ._warnings import _warn  from .gui import Gui  from .state import State      def download(      state: State, content: t.Any, name: t.Optional[str] = "", on_action: t.Optional[t.Union[str, t.Callable]] = ""  ):      """Download content to the client.        Arguments:          state (State^): The current user state as received in any callback.          content: File path or file content. See below.          name: File name for the content on the client browser (defaults to content name).          on_action: Callback function (or callback name) to call when the download ends. See below.        ## Notes:        - *content*: this parameter can hold several values depending on your use case:          - a string: the value must be an existing path name to the file that gets downloaded or            the URL to the resource you want to download.          - a buffer (such as a `bytes` object): if the size of the buffer is smaller than             the [*data_url_max_size*](../gui/configuration.md#p-data_url_max_size) configuration             setting, then the [`python-magic`](https://pypi.org/project/python-magic/) package is             used to determine the [MIME type](https://en.wikipedia.org/wiki/Media_type) of the             buffer content, and the download is performed using a generated "data:" URL with             the relevant type, and a base64-encoded version of the buffer content.
             If the buffer is too large, its content is transferred after saving it in a temporary             server file.        - *on_action*: this callback is triggered when the transfer of the content is achieved.
             In this function, you can perform any clean-up operation that could be required after             the download is completed.
             This callback can use three optional parameters:             - *state*: the `State^` instance of the caller.             - *id* (optional): a string representing the identifier of the caller. If this function               is called directly, this will always be "Gui.download". Some controls may also trigger               download actions, and then *id* would reflect the identifier of those controls.             - *payload* (optional): an optional payload from the caller.
               This is a dictionary with the following keys:                - *action*: the name of the callback;                - *args*: an array of two strings. The first element reflects the *name* parameter,                  and the second element reflects the server-side URL where the file is located.      """      if state and isinstance(state._gui, Gui):          state._gui._download(content, name, on_action)      else:          _warn("'download()' must be called in the context of a callback.")      def notify(      state: State,      notification_type: str = "I",      message: str = "",      system_notification: t.Optional[bool] = None,      duration: t.Optional[int] = None,  ):      """Send a notification to the user interface.        Arguments:          state (State^): The current user state as received in any callback.          notification_type: The notification type. This can be one of "success", "info",              "warning", or "error".
              To remove the last notification, set this parameter to the empty string.          message: The text message to display.          system_notification: If True, the system will also show the notification.
              If not specified or set to None, this parameter will use the value of              *configuration[system_notification]*.          duration: The time, in milliseconds, during which the notification is shown.              If not specified or set to None, this parameter will use the value of              *configuration[notification_duration]*.        Note that you can also call this function with *notification_type* set to the first letter      or the alert type (i.e. setting *notification_type* to "i" is equivalent to setting it to      "info").        If *system_notification* is set to True, then the browser requests the system      to display a notification as well. They usually appear in small windows that      fly out of the system tray.
      The first time your browser is requested to show such a system notification for      Taipy applications, you may be prompted to authorize the browser to do so. Please      refer to your browser documentation for details on how to allow or prevent this      feature.      """      if state and isinstance(state._gui, Gui):          state._gui._notify(notification_type, message, system_notification, duration)      else:          _warn("'notify()' must be called in the context of a callback.")      def hold_control(      state: State,      callback: t.Optional[t.Union[str, t.Callable]] = None,      message: t.Optional[str] = "Work in Progress...",  ):      """Hold the User Interface actions.        When the User Interface is held, users cannot interact with visual elements.
      The application must call `resume_control()^` so that users can interact again      with the visual elements.        You can set a callback function (or the name of a function) in the *callback* parameter. Then,      a "Cancel" button will be displayed so the user can cancel whatever is happening in the      application. When pressed, the callback is invoked.        Arguments:          state (State^): The current user state received in any callback.          callback (Optional[Union[str, Callable]]): The function to be called if the user              chooses to cancel.
              If empty or None, no cancel action is provided to the user.
              The signature of this function is:              - state (State^): The user state;              - id (str): the id of the button that triggered the callback. That will always be                "UIBlocker" since it is created and managed internally;          message: The message to show. The default value is the string "Work in Progress...".      """      if state and isinstance(state._gui, Gui):          state._gui._hold_actions(callback, message)      else:          _warn("'hold_actions()' must be called in the context of a callback.")      def resume_control(state: State):      """Resume the User Interface actions.        This function must be called after `hold_control()^` was invoked, when interaction      must be allowed again for the user.        Arguments:          state (State^): The current user state as received in any callback.      """      if state and isinstance(state._gui, Gui):          state._gui._resume_actions()      else:          _warn("'resume_actions()' must be called in the context of a callback.")      def navigate(      state: State,      to: t.Optional[str] = "",      params: t.Optional[t.Dict[str, str]] = None,      tab: t.Optional[str] = None,      force: t.Optional[bool] = False,  ):      """Navigate to a page.        Arguments:          state (State^): The current user state as received in any callback.          to: The name of the page to navigate to. This can be a page identifier (as created by              `Gui.add_page()^` with no leading '/') or an URL.
              If omitted, the application navigates to the root page.          params: A dictionary of query parameters.          tab: When navigating to a page that is not a known page, the page is opened in a tab identified by              *tab* (as in [window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)).
              The default value creates a new tab for the page (which is equivalent to setting *tab* to "_blank").          force: When navigating to a known page, the content is refreshed even it the page is already shown.      """      if state and isinstance(state._gui, Gui):          state._gui._navigate(to, params, tab, force)      else:          _warn("'navigate()' must be called in the context of a callback.")      def get_user_content_url(      state: State, path: t.Optional[str] = None, params: t.Optional[t.Dict[str, str]] = None  ) -> t.Optional[str]:      """Get a user content URL.        This function can be used if you need to deliver dynamic content to your page: you can create      a path at run-time that, when queried, will deliver some user-defined content defined in the      *on_user_content* callback (see the description of the `Gui^` class for more information).        Arguments:          state (State^): The current user state as received in any callback.          path: An optional additional path to the URL.          params: An optional dictionary sent to the *on_user_content* callback.
              These arguments are added as query parameters to the generated URL and converted into              strings.        Returns:          An URL that, when queried, triggers the *on_user_content* callback.      """      if state and isinstance(state._gui, Gui):          return state._gui._get_user_content_url(path, params)      _warn("'get_user_content_url()' must be called in the context of a callback.")      return None      def get_state_id(state: State) -> t.Optional[str]:      """Get the identifier of a state.        The state identifier is a string generated by Taipy GUI for a given `State^` that is used      to serialize callbacks.      See the      [User Manual section on Long Running Callbacks](../gui/callbacks.md#long-running-callbacks)      for details on when and how this function can be used.        Arguments:          state (State^): The current user state as received in any callback.        Returns:          A string that uniquely identifies the state.
          If None, then **state** was not handled by a `Gui^` instance.      """      if state and isinstance(state._gui, Gui):          return state._gui._get_client_id()      return None      def get_module_context(state: State) -> t.Optional[str]:      """Get the name of the module currently in used when using page scopes        Arguments:          state (State^): The current user state as received in any callback.        Returns:          The name of the current module      """      if state and isinstance(state._gui, Gui):          return state._gui._get_locals_context()      return None      def get_context_id(state: State) -> t.Any:      _warn("'get_context_id()' was deprecated in Taipy GUI 2.0. Use 'get_state_id()' instead.")      return get_state_id(state)      def get_module_name_from_state(state: State) -> t.Optional[str]:      """Get the module name that triggered a callback.        Pages can be defined in different modules yet refer to callback functions declared elsewhere      (typically, the application's main module).        This function returns the name of the module where the page that holds the control that      triggered the callback was declared.  This lets applications implement different behaviors      depending on what page is involved.        This function must be called only in the body of a callback function.        Arguments:          state (State^): The `State^` instance, as received in any callback.        Returns:          The name of the module that holds the definition of the page containing the control              that triggered the callback that was provided the *state* object.      """      if state and isinstance(state._gui, Gui):          return state._gui._get_locals_context()      return None      def invoke_callback(      gui: Gui,      state_id: str,      callback: t.Callable,      args: t.Union[t.Tuple, t.List],      module_context: t.Optional[str] = None,  ) -> t.Any:      """Invoke a user callback for a given state.        See the      [User Manual section on Long Running Callbacks in a Thread](../gui/callbacks.md#long-running-callbacks-in-a-thread)      for details on when and how this function can be used.        Arguments:          gui (Gui^): The current Gui instance.          state_id: The identifier of the state to use, as returned by `get_state_id()^`.          callback (Callable[[State^, ...], None]): The user-defined function that is invoked.
              The first parameter of this function **must** be a `State^`.          args (Union[Tuple, List]): The remaining arguments, as a List or a Tuple.          module_context (Optional[str]): the name of the module that will be used.      """      if isinstance(gui, Gui):          return gui._call_user_callback(state_id, callback, list(args), module_context)      _warn("'invoke_callback()' must be called with a valid Gui instance.")      def broadcast_callback(      gui: Gui,      callback: t.Callable,      args: t.Optional[t.Union[t.Tuple, t.List]] = None,      module_context: t.Optional[str] = None,  ) -> t.Any:      """Invoke a callback for every client.        This callback gets invoked for every client connected to the application with the appropriate      `State^` instance. You can then perform client-specific tasks, such as updating the state      variable reflected in the user interface.        Arguments:          gui (Gui^): The current Gui instance.          callback: The user-defined function to be invoked.
              The first parameter of this function must be a `State^` object representing the              client for which it is invoked.
              The other parameters should reflect the ones provided in the *args* collection.          args: The parameters to send to *callback*, if any.      """      if isinstance(gui, Gui):          return gui._call_broadcast_callback(callback, list(args) if args else [], module_context)      _warn("'broadcast_callback()' must be called with a valid Gui instance.")      def invoke_state_callback(gui: Gui, state_id: str, callback: t.Callable, args: t.Union[t.Tuple, t.List]) -> t.Any:      _warn("'invoke_state_callback()' was deprecated in Taipy GUI 2.0. Use 'invoke_callback()' instead.")      return invoke_callback(gui, state_id, callback, args)      def invoke_long_callback(      state: State,      user_function: t.Callable,      user_function_args: t.Union[t.Tuple, t.List] = [],      user_status_function: t.Optional[t.Callable] = None,      user_status_function_args: t.Union[t.Tuple, t.List] = [],      period=0,  ):      """Invoke a long running user callback.        Long-running callbacks are run in a separate thread to not block the application itself.        This function expects to be provided a function to run in the background (in *user_function*).
      It can also be specified a *status function* that is called when the operation performed by      *user_function* is finished (successfully or not), or periodically (using the *period* parameter).        See the      [User Manual section on Long Running Callbacks](../gui/callbacks.md#long-running-callbacks)      for details on when and how this function can be used.        Arguments:          state (State^): The `State^` instance, as received in any callback.          user_function (Callable[[...], None]): The function that will be run independently of Taipy GUI. Note              that this function must not use *state*, which is not persisted across threads.          user_function_args (Optional[List|Tuple]): The arguments to send to *user_function*.          user_status_function (Optional(Callable[[State^, bool, ...], None])): The optional user-defined status              function that is invoked at the end of and possibly during the runtime of *user_function*:                - The first parameter of this function is set to a `State^` instance.              - The second parameter of this function is set to a bool or an int, depending on the                conditions under which it is called:                   - If this parameter is set to a bool value, then:                       - If True, this indicates that *user_function* has finished properly.                          The last argument passed will be the result of the user_function call.                     - If False, this indicates that *user_function* failed.                   - If this parameter is set to an int value, then this value indicates                   how many periods (as lengthy as indicated in *period*) have elapsed since *user_function* was                   started.          user_status_function_args (Optional[List|Tuple]): The remaining arguments of the user status function.          period (int): The interval, in milliseconds, at which *user_status_function* is called.
              The default value is 0, meaning no call to *user_status_function* will happen until *user_function*              terminates (then the second parameter of that call will be ).
              When set to a value smaller than 500, *user_status_function* is called only when *user_function*              terminates (as if *period* was set to 0).      """      if not state or not isinstance(state._gui, Gui):          _warn("'invoke_long_callback()' must be called in the context of a callback.")          return        state_id = get_state_id(state)      module_context = get_module_context(state)      if not isinstance(state_id, str) or not isinstance(module_context, str):          return        this_gui = state._gui        def callback_on_exception(state: State, function_name: str, e: Exception):          if not this_gui._call_on_exception(function_name, e):              _warn(f"invoke_long_callback(): Exception raised in function {function_name}()", e)        def callback_on_status(          status: t.Union[int, bool],          e: t.Optional[Exception] = None,          function_name: t.Optional[str] = None,          function_result: t.Optional[t.Any] = None,      ):          if callable(user_status_function):              invoke_callback(                  this_gui,                  str(state_id),                  user_status_function,                  [status] + list(user_status_function_args) + [function_result],  # type: ignore                  str(module_context),              )          if e:              invoke_callback(                  this_gui,                  str(state_id),                  callback_on_exception,                  (                      str(function_name),                      e,                  ),                  str(module_context),              )        def user_function_in_thread(*uf_args):          try:              res = user_function(*uf_args)              callback_on_status(True, function_result=res)          except Exception as e:              callback_on_status(False, e, user_function.__name__)        def thread_status(name: str, period_s: float, count: int):          active_thread = next((t for t in threading.enumerate() if t.name == name), None)          if active_thread:              callback_on_status(count)              threading.Timer(period_s, thread_status, (name, period_s, count + 1)).start()        thread = threading.Thread(target=user_function_in_thread, args=user_function_args)      thread.start()      if isinstance(period, int) and period >= 500 and callable(user_status_function):          thread_status(thread.name, period / 1000.0, 0)


import typing as t  from copy import copy    from taipy.config import Config as TaipyConfig  from taipy.config import UniqueSection    from ._default_config import default_config      class _GuiSection(UniqueSection):      name = "gui"        def __init__(self, property_list: t.Optional[t.List] = None, **properties):          self._property_list = property_list          super().__init__(**properties)        def __copy__(self):          return _GuiSection(property_list=copy(self._property_list), **copy(self._properties))        def _clean(self):          self._properties.clear()        def _to_dict(self):          as_dict = {}          as_dict.update(self._properties)          return as_dict        @classmethod      def _from_dict(cls, as_dict: t.Dict[str, t.Any], *_):          return _GuiSection(property_list=list(default_config), **as_dict)        def _update(self, as_dict: t.Dict[str, t.Any]):          if self._property_list:              as_dict = {k: v for k, v in as_dict.items() if k in self._property_list}          self._properties.update(as_dict)        @staticmethod      def _configure(**properties) -> "_GuiSection":          """NOT DOCUMENTED          Configure the Graphical User Interface.            Parameters:              **properties (dict[str, any]): Keyword arguments that configure the behavior of the `Gui^` instances.<br/>                  Please refer to the                  [Configuration section](../gui/configuration.md#configuring-the-gui-instance)                  of the User Manual for more information on the accepted arguments.          Returns:              The GUI configuration.            """          section = _GuiSection(property_list=list(default_config), **properties)          TaipyConfig._register(section)          return TaipyConfig.unique_sections[_GuiSection.name]


# _Page for multipage support  from __future__ import annotations    import logging  import typing as t  import warnings    if t.TYPE_CHECKING:      from ._renderers import Page      from .gui import Gui      class _Page(object):      def __init__(self):          self._rendered_jsx: t.Optional[str] = None          self._renderer: t.Optional[Page] = None          self._style: t.Optional[str] = None          self._route: t.Optional[str] = None          self._head: t.Optional[list] = None        def render(self, gui: Gui):          if self._renderer is None:              raise RuntimeError(f"Can't render page {self._route}: no renderer found")          with warnings.catch_warnings(record=True) as w:              warnings.resetwarnings()              with gui._set_locals_context(self._renderer._get_module_name()):                  self._rendered_jsx = self._renderer.render(gui)              if w:                  s = "\033[1;31m\n"                  s += (                      message := f"--- {len(w)} warning(s) were found for page '{'/' if self._route == gui._get_root_page_name() else self._route}' {self._renderer._get_content_detail(gui)} ---\n"  # noqa: E501                  )                  for i, wm in enumerate(w):                      s += f" - Warning {i + 1}: {wm.message}\n"                  s += "-" * len(message)                  s += "\033[0m\n"                  logging.warn(s)          if hasattr(self._renderer, "head"):              self._head = list(self._renderer.head)  # type: ignore          # return renderer module_name from frame          return self._renderer._get_module_name()


#!/usr/bin/env python    """The setup script."""    import json  import os  from pathlib import Path    from setuptools import find_namespace_packages, find_packages, setup  from setuptools.command.build_py import build_py    readme = Path("README.md").read_text()    with open(f"src{os.sep}taipy{os.sep}gui{os.sep}version.json") as version_file:      version = json.load(version_file)      version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'      if vext := version.get("ext"):          version_string = f"{version_string}.{vext}"    requirements = [      "flask>=3.0.0,<3.1",      "flask-cors>=4.0.0,<5.0",      "flask-socketio>=5.3.6,<6.0",      "markdown>=3.4.4,<4.0",      "pandas>=2.0.0,<3.0",      "python-dotenv>=1.0.0,<1.1",      "pytz>=2021.3,<2022.2",      "tzlocal>=3.0,<5.0",      "backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'",      "gevent>=23.7.0,<24.0",      "gevent-websocket>=0.10.1,<0.11",      "kthread>=0.2.3,<0.3",      "taipy-config@git+https://git@github.com/Avaiga/taipy-config.git@develop",      "gitignore-parser>=0.1,<0.2",      "simple-websocket>=0.10.1,<1.0",      "twisted>=23.8.0,<24.0",  ]    test_requirements = ["pytest>=3.8"]    extras_require = {      "ngrok": ["pyngrok>=5.1,<6.0"],      "image": [          "python-magic>=0.4.24,<0.5;platform_system!='Windows'",          "python-magic-bin>=0.4.14,<0.5;platform_system=='Windows'",      ],      "arrow": ["pyarrow>=10.0.1,<11.0"],  }      def _build_webapp():      already_exists = Path("./src/taipy/gui/webapp/index.html").exists()      if not already_exists:          os.system("cd ../../../frontend/taipy-gui/dom && npm ci")          os.system("cd ../../../frontend/taipy-gui && npm ci --omit=optional && npm run build")      class NPMInstall(build_py):      def run(self):          _build_webapp()          build_py.run(self)      setup(      author="Avaiga",      author_email="dev@taipy.io",      python_requires=">=3.8",      classifiers=[          "Intended Audience :: Developers",          "License :: OSI Approved :: Apache Software License",          "Natural Language :: English",          "Programming Language :: Python :: 3",          "Programming Language :: Python :: 3.8",          "Programming Language :: Python :: 3.9",          "Programming Language :: Python :: 3.10",          "Programming Language :: Python :: 3.11",      ],      description="Low-code library to create graphical user interfaces on the Web for your Python applications.",      long_description=readme,      long_description_content_type="text/markdown",      install_requires=requirements,      license="Apache License 2.0",      include_package_data=True,      keywords="taipy-gui",      name="taipy-gui",      package_dir={"": "src"},      packages=find_namespace_packages(where="src") + find_packages(include=["taipy", "taipy.gui", "taipy.gui.*"]),      test_suite="tests",      tests_require=test_requirements,      url="https://github.com/avaiga/taipy-gui",      version=version_string,      zip_safe=False,      extras_require=extras_require,      cmdclass={"build_py": NPMInstall},  )


from .config import Config, Stylekit    _default_stylekit: Stylekit = {      # Primary and secondary colors      "color_primary": "#ff6049",      "color_secondary": "#293ee7",      # Contextual color      "color_error": "#FF595E",      "color_warning": "#FAA916",      "color_success": "#96E6B3",      # Background and elevation color for LIGHT MODE      "color_background_light": "#f0f5f7",      "color_paper_light": "#ffffff",      # Background and elevation color for DARK MODE      "color_background_dark": "#152335",      "color_paper_dark": "#1f2f44",      # DEFINING FONTS      # Set main font family      "font_family": "Lato, Arial, sans-serif",      # DEFINING ROOT STYLES      # Set root margin      "root_margin": "1rem",      # DEFINING SHAPES      # Base border radius in px      "border_radius": 8,      # DEFINING MUI COMPONENTS STYLES      # Matching input and button height in css size unit      "input_button_height": "48px",  }    # Default config loaded by app.py  default_config: Config = {      "allow_unsafe_werkzeug": False,      "async_mode": "gevent",      "change_delay": None,      "chart_dark_template": None,      "base_url": "/",      "dark_mode": True,      "dark_theme": None,      "debug": False,      "extended_status": False,      "favicon": None,      "flask_log": False,      "host": "127.0.0.1",      "light_theme": None,      "margin": "1em",      "ngrok_token": "",      "notebook_proxy": True,      "notification_duration": 3000,      "propagate": True,      "run_browser": True,      "run_in_thread": False,      "run_server": True,      "server_config": None,      "single_client": False,      "system_notification": False,      "theme": None,      "time_zone": None,      "title": None,      "stylekit": _default_stylekit.copy(),      "upload_folder": None,      "use_arrow": False,      "use_reloader": False,      "watermark": "Taipy inside",      "webapp_path": None,      "port": 5000,  }


from __future__ import annotations    import typing as t    from ._page import _Page  from ._warnings import _warn  from .state import State    if t.TYPE_CHECKING:      from .page import Page      class Partial(_Page):      """Re-usable Page content.        Partials are used when you need to use a partial page content in different      and not related pages. This allows not to have to repeat yourself when      creating your page templates.        Visual elements such as [`part`](../gui/viselements/part.md),      [`dialog`](../gui/viselements/dialog.md) or      [`pane`](../gui/viselements/pane.md) can use Partials.        Note that `Partial` has no constructor (no `__init__()` method): to create a      `Partial`, you must call the `Gui.add_partial()^` function.        Partials can be really handy if you want to modify a section of a page:      the `update_content()` method dynamically update pages that make use      of this `Partial` therefore making it easy to change the content of      any page, at any moment.      """        _PARTIALS = "__partials"        __partials: t.Dict[str, Partial] = {}        def __init__(self, route: t.Optional[str] = None):          super().__init__()          if route is None:              self._route = f"TaiPy_partials_{len(Partial.__partials)}"              Partial.__partials[self._route] = self          else:              self._route = route        def update_content(self, state: State, content: str | "Page"):          """Update partial content.            Arguments:              state (State^): The current user state as received in any callback.              content (str): The new content to use and display.          """          if state and state._gui and callable(state._gui._update_partial):              state._gui._update_partial(self.__copy(content))          else:              _warn("'Partial.update_content()' must be called in the context of a callback.")        def __copy(self, content: str | "Page") -> Partial:          new_partial = Partial(self._route)          from .page import Page            if isinstance(content, Page):              new_partial._renderer = content          else:              new_partial._renderer = type(self._renderer)(content=content) if self._renderer is not None else None          return new_partial


from typing import Dict, Tuple    from taipy._cli._base_cli import _CLI      class _GuiCLI:      """Command-line interface of GUI."""        __GUI_ARGS: Dict[Tuple, Dict] = {          ("--port", "-P"): {              "dest": "taipy_port",              "metavar": "PORT",              "nargs": "?",              "default": "",              "const": "",              "help": "Specify server port",          },          ("--host", "-H"): {              "dest": "taipy_host",              "metavar": "HOST",              "nargs": "?",              "default": "",              "const": "",              "help": "Specify server host",          },          ("--ngrok-token",): {              "dest": "taipy_ngrok_token",              "metavar": "NGROK_TOKEN",              "nargs": "?",              "default": "",              "const": "",              "help": "Specify NGROK Authtoken",          },          ("--webapp-path",): {              "dest": "taipy_webapp_path",              "metavar": "WEBAPP_PATH",              "nargs": "?",              "default": "",              "const": "",              "help": "The path to the web app to be used. The default is the webapp directory under gui in the Taipy GUI package directory.",  # noqa: E501          },      }        __DEBUG_ARGS: Dict[str, Dict] = {          "--debug": {"dest": "taipy_debug", "help": "Turn on debug", "action": "store_true"},          "--no-debug": {"dest": "taipy_no_debug", "help": "Turn off debug", "action": "store_true"},      }        __RELOADER_ARGS: Dict[str, Dict] = {          "--use-reloader": {"dest": "taipy_use_reloader", "help": "Auto reload on code changes", "action": "store_true"},          "--no-reloader": {"dest": "taipy_no_reloader", "help": "No reload on code changes", "action": "store_true"},      }        @classmethod      def create_parser(cls):          gui_parser = _CLI._add_groupparser("Taipy GUI", "Optional arguments for Taipy GUI service")            for args, arg_dict in cls.__GUI_ARGS.items():              taipy_arg = (args[0], cls.__add_taipy_prefix(args[0]), *args[1:])              gui_parser.add_argument(*taipy_arg, **arg_dict)            debug_group = gui_parser.add_mutually_exclusive_group()          for debug_arg, debug_arg_dict in cls.__DEBUG_ARGS.items():              debug_group.add_argument(debug_arg, cls.__add_taipy_prefix(debug_arg), **debug_arg_dict)            reloader_group = gui_parser.add_mutually_exclusive_group()          for reloader_arg, reloader_arg_dict in cls.__RELOADER_ARGS.items():              reloader_group.add_argument(reloader_arg, cls.__add_taipy_prefix(reloader_arg), **reloader_arg_dict)        @classmethod      def create_run_parser(cls):          run_parser = _CLI._add_subparser("run", help="Run a Taipy application.")          for args, arg_dict in cls.__GUI_ARGS.items():              run_parser.add_argument(*args, **arg_dict)            debug_group = run_parser.add_mutually_exclusive_group()          for debug_arg, debug_arg_dict in cls.__DEBUG_ARGS.items():              debug_group.add_argument(debug_arg, **debug_arg_dict)            reloader_group = run_parser.add_mutually_exclusive_group()          for reloader_arg, reloader_arg_dict in cls.__RELOADER_ARGS.items():              reloader_group.add_argument(reloader_arg, **reloader_arg_dict)        @classmethod      def parse_arguments(cls):          return _CLI._parse()        @classmethod      def __add_taipy_prefix(cls, key: str):          if key.startswith("--no-"):              return key[:5] + "taipy-" + key[5:]            return key[:2] + "taipy-" + key[2:]