content: import typing as _t
  
  from importlib import metadata as _importlib_metadata
  
  import typing_extensions as _tx
  import flask as _f
  
  from .exceptions import HookError
  from .resources import ResourceType
  from ._callback import ClientsideFuncType
  
  if _t.TYPE_CHECKING:
      from .dash import Dash
      from .development.base_component import Component
  
      ComponentType = _t.TypeVar("ComponentType", bound=Component)
      LayoutType = _t.Union[ComponentType, _t.List[ComponentType]]
  else:
      LayoutType = None
      ComponentType = None
      Dash = None
  
  
  HookDataType = _tx.TypeVar("HookDataType")
  DevtoolPosition = _tx.Literal["right", "left"]
  
  
  # pylint: disable=too-few-public-methods
  class _Hook(_tx.Generic[HookDataType]):
      def __init__(
          self, func, priority=0, final=False, data: _t.Optional[HookDataType] = None
      ):
          self.func = func
          self.final = final
          self.data = data
          self.priority = priority
  
      def __call__(self, *args, **kwargs):
          return self.func(*args, **kwargs)
  
  
  class _Hooks:
      def __init__(self) -> None:
          self._ns: _t.Dict[str, _t.List[_t.Any]] = {
              "setup": [],
              "layout": [],
              "routes": [],
              "error": [],
              "callback": [],
              "index": [],
              "custom_data": [],
              "dev_tools": [],
          }
          self._js_dist: _t.List[_t.Any] = []
          self._css_dist: _t.List[_t.Any] = []
          self._clientside_callbacks: _t.List[
              _t.Tuple[ClientsideFuncType, _t.Any, _t.Any]
          ] = []
  
          # final hooks are a single hook added to the end of regular hooks.
          self._finals: _t.Dict[str, _t.Any] = {}
  
      def add_hook(
          self,
          hook: str,
          func: _t.Callable,
          priority: _t.Optional[int] = None,
          final: bool = False,
          data: _t.Optional[HookDataType] = None,
      ):
          if final:
              existing = self._finals.get(hook)
              if existing:
                  raise HookError("Final hook already present")
              self._finals[hook] = _Hook(func, final, data=data)
              return
          hks = self._ns.get(hook, [])
  
          p = priority or 0
          if not priority and len(hks):
              # Take the minimum value and remove 1 to keep the order.
              priority_min = min(h.priority for h in hks)
              p = priority_min - 1
  
          hks.append(_Hook(func, priority=p, data=data))
          self._ns[hook] = sorted(hks, reverse=True, key=lambda h: h.priority)
  
      def get_hooks(self, hook: str) -> _t.List[_Hook]:
          final = self._finals.get(hook, None)
          if final:
              final = [final]
          else:
              final = []
          return self._ns.get(hook, []) + final
  
      def layout(self, priority: _t.Optional[int] = None, final: bool = False):
          """
          Run a function when serving the layout, the return value
          will be used as the layout.
          """
  
          def _wrap(func: _t.Callable[[LayoutType], LayoutType]):
              self.add_hook("layout", func, priority=priority, final=final)
              return func
  
          return _wrap
  
      def setup(self, priority: _t.Optional[int] = None, final: bool = False):
          """
          Can be used to get a reference to the app after it is instantiated.
          """
  
          def _setup(func: _t.Callable[[Dash], None]):
              self.add_hook("setup", func, priority=priority, final=final)
              return func
  
          return _setup
  
      def route(
          self,
          name: _t.Optional[str] = None,
          methods: _t.Sequence[str] = ("GET",),
          priority: _t.Optional[int] = None,
          final: bool = False,
      ):
          """
          Add a route to the Dash server.
          """
  
          def wrap(func: _t.Callable[[], _f.Response]):
              _name = name or func.__name__
              self.add_hook(
                  "routes",
                  func,
                  priority=priority,
                  final=final,
                  data=dict(name=_name, methods=methods),
              )
              return func
  
          return wrap
  
      def error(self, priority: _t.Optional[int] = None, final: bool = False):
          """Automatically add an error handler to the dash app."""
  
          def _error(func: _t.Callable[[Exception], _t.Any]):
              self.add_hook("error", func, priority=priority, final=final)
              return func
  
          return _error
  
      def callback(
          self, *args, priority: _t.Optional[int] = None, final: bool = False, **kwargs
      ):
          """
          Add a callback to all the apps with the hook installed.
          """
  
          def wrap(func):
              self.add_hook(
                  "callback",
                  func,
                  priority=priority,
                  final=final,
                  data=(list(args), dict(kwargs)),
              )
              return func
  
          return wrap
  
      def clientside_callback(
          self, clientside_function: ClientsideFuncType, *args, **kwargs
      ):
          """
          Add a callback to all the apps with the hook installed.
          """
          self._clientside_callbacks.append((clientside_function, args, kwargs))
  
      def script(self, distribution: _t.List[ResourceType]):
          """Add js scripts to the page."""
          self._js_dist.extend(distribution)
  
      def stylesheet(self, distribution: _t.List[ResourceType]):
          """Add stylesheets to the page."""
          self._css_dist.extend(distribution)
  
      def index(self, priority: _t.Optional[int] = None, final=False):
          """Modify the index of the apps."""
  
          def wrap(func):
              self.add_hook(
                  "index",
                  func,
                  priority=priority,
                  final=final,
              )
              return func
  
          return wrap
  
      def custom_data(
          self, namespace: str, priority: _t.Optional[int] = None, final=False
      ):
          """
          Add data to the callback_context.custom_data property under the namespace.
  
          The hook function takes the current context_value and before the ctx is set
          and has access to the flask request context.
          """
  
          def wrap(func: _t.Callable[[_t.Dict], _t.Any]):
              self.add_hook(
                  "custom_data",
                  func,
                  priority=priority,
                  final=final,
                  data={"namespace": namespace},
              )
              return func
  
          return wrap
  
      def devtool(
          self,
          namespace: str,
          component_type: str,
          props=None,
          position: DevtoolPosition = "right",
      ):
          """
          Add a component to be rendered inside the dev tools.
  
          If it's a dash component, it can be used in callbacks provided
          that it has an id and the dependency is set with allow_optional=True.
  
          `props` can be a function, in which case it will be called before
          sending the component to the frontend.
          """
          self._ns["dev_tools"].append(
              {
                  "namespace": namespace,
                  "type": component_type,
                  "props": props or {},
                  "position": position,
              }
          )
  
  
  hooks = _Hooks()
  
  
  class HooksManager:
      # Flag to only run `register_setuptools` once
      _registered = False
      hooks = hooks
  
      # pylint: disable=too-few-public-methods
      class HookErrorHandler:
          def __init__(self, original):
              self.original = original
  
          def __call__(self, err: Exception):
              result = None
              if self.original:
                  result = self.original(err)
              hook_result = None
              for hook in HooksManager.get_hooks("error"):
                  hook_result = hook(err)
              return result or hook_result
  
      @classmethod
      def get_hooks(cls, hook: str):
          return cls.hooks.get_hooks(hook)
  
      @classmethod
      def register_setuptools(cls):
          if cls._registered:
              # Only have to register once.
              return
  
          for dist in _importlib_metadata.distributions():
              for entry in dist.entry_points:
                  # Look for setup.py entry points named `dash_hooks`
                  if entry.group != "dash_hooks":
                      continue
                  entry.load()
  