code: def test__lifecycle_hooks(ManagedServerLoop: MSL) -> None:
      application = Application()
      handler = HookTestHandler()
      application.add(handler)
      with ManagedServerLoop(
          application, check_unused_sessions_milliseconds=30
      ) as server:
          client_session = pull_session(
              session_id=ID("test__lifecycle_hooks"),
              url=url(server),
              io_loop=server.io_loop,
          )
          client_doc = client_session.document
          assert len(client_doc.roots) == 1
  
          server_session = server.get_session("/", client_session.id)
          server_doc = server_session.document
          assert len(server_doc.roots) == 1
  
          # save for later, since doc.roots will be emptied after the session is closed
          client_hook_list = list(client_doc.roots[0].hooks)
          server_hook_list = list(server_doc.roots[0].hooks)
  
          client_session.close()
  
          # expire the session quickly rather than after the usual timeout
          server_session.request_expiration()
  
          server.io_loop.call_later(0.1, lambda: server.io_loop.stop())
  
          server.io_loop.start()
  
      assert handler.hooks == [
          "server_loaded",
          "session_created",
          "modify",
          "next_tick",
          "timeout",
          "periodic",
          "session_destroyed",
          "server_unloaded",
      ]
  
      assert handler.load_count == 1
      assert handler.unload_count == 1
  
      # 3 instead of 6, because locked callbacks on destroyed sessions become no-ops
      assert handler.session_creation_async_value == 3
      assert client_doc.title == "Modified"
      assert server_doc.title == "Modified"
  
      # only the handler sees "session_destroyed" since the session is shut down at that point.
      assert client_hook_list == ["session_created", "modify"]
      assert server_hook_list == ["session_created", "modify"]
  
imports: from __future__ import annotations
  import pytest
  import asyncio
  import logging
  import re
  import ssl
  import sys
  import time
  from datetime import timedelta
  from unittest import mock
  import tornado
  from _util_server import http_get, url, websocket_open, ws_url
  from tornado.httpclient import HTTPError
  from tornado.httpserver import HTTPServer
  from tornado.ioloop import IOLoop
  from bokeh.application import Application
  from bokeh.application.handlers import Handler
  from bokeh.client import pull_session
  from bokeh.core.properties import List, String
  from bokeh.core.types import ID
  from bokeh.model import Model
  from bokeh.server.server import BaseServer, Server
  from bokeh.server.tornado import BokehTornado
  from bokeh.util.token import check_token_signature, generate_jwt_token, get_session_id, get_token_payload
  from tests.support.plugins.managed_server_loop import MSL
  import bokeh.server.server
label_desc: Use image node_id 10 for calling a global function with example usage: test__lifecycle_hooks(ManagedServerLoop) without return types