class_docstr: Subcommand to launch the Bokeh server.
  
      
docstr_tok: []
code: def invoke(self, args: argparse.Namespace) -> None:
      """ """
      basicConfig(format=args.log_format, filename=args.log_file)
  
      # This is a bit of a fudge. We want the default log level for non-server
      # cases to be None, i.e. we don't set a log level. But for the server we
      # do want to set the log level to INFO if nothing else overrides that.
      log_level = settings.py_log_level(args.log_level)
      if log_level is None:
          log_level = logging.INFO
      logging.getLogger("bokeh").setLevel(log_level)
  
      if args.use_config is not None:
          log.info(f"Using override config file: {args.use_config}")
          settings.load_config(args.use_config)
  
      # protect this import inside a function so that "bokeh info" can work
      # even if Tornado is not installed
      from bokeh.server.server import Server
  
      files: list[str] = []
      for f in args.files:
          if args.glob:
              files.extend(glob(f))
          else:
              files.append(f)
  
      argvs = {f: args.args for f in files}
      applications = build_single_handler_applications(files, argvs)
  
      if len(applications) == 0:
          # create an empty application by default
          applications["/"] = Application()
  
      # rename args to be compatible with Server
      if args.keep_alive is not None:
          args.keep_alive_milliseconds = args.keep_alive
  
      if args.check_unused_sessions is not None:
          args.check_unused_sessions_milliseconds = (
              args.check_unused_sessions
          )
  
      if args.unused_session_lifetime is not None:
          args.unused_session_lifetime_milliseconds = (
              args.unused_session_lifetime
          )
  
      if args.stats_log_frequency is not None:
          args.stats_log_frequency_milliseconds = (
              args.stats_log_frequency
          )
  
      if args.mem_log_frequency is not None:
          args.mem_log_frequency_milliseconds = args.mem_log_frequency
  
      server_kwargs = {
          key: getattr(args, key)
          for key in [
              "port",
              "address",
              "unix_socket",
              "allow_websocket_origin",
              "num_procs",
              "prefix",
              "index",
              "keep_alive_milliseconds",
              "check_unused_sessions_milliseconds",
              "unused_session_lifetime_milliseconds",
              "stats_log_frequency_milliseconds",
              "mem_log_frequency_milliseconds",
              "use_xheaders",
              "websocket_max_message_size",
              "websocket_compression_level",
              "websocket_compression_mem_level",
              "include_cookies",
              "include_headers",
              "exclude_cookies",
              "exclude_headers",
              "session_token_expiration",
          ]
          if getattr(args, key, None) is not None
      }
  
      server_kwargs["sign_sessions"] = settings.sign_sessions()
      server_kwargs["secret_key"] = settings.secret_key_bytes()
      server_kwargs["ssl_certfile"] = settings.ssl_certfile(
          getattr(args, "ssl_certfile", None)
      )
      server_kwargs["ssl_keyfile"] = settings.ssl_keyfile(
          getattr(args, "ssl_keyfile", None)
      )
      server_kwargs["ssl_password"] = settings.ssl_password()
      server_kwargs["generate_session_ids"] = True
      if args.session_ids is None:
          # no --session-ids means use the env vars
          pass
      elif args.session_ids == "unsigned":
          server_kwargs["sign_sessions"] = False
      elif args.session_ids == "signed":
          server_kwargs["sign_sessions"] = True
      elif args.session_ids == "external-signed":
          server_kwargs["sign_sessions"] = True
          server_kwargs["generate_session_ids"] = False
      else:
          raise RuntimeError(
              "argparse should have filtered out --session-ids mode "
              + args.session_ids
          )
  
      if (
          server_kwargs["sign_sessions"]
          and not server_kwargs["secret_key"]
      ):
          die(
              "To sign sessions, the BOKEH_SECRET_KEY environment variable must be set; "
              + "the `bokeh secret` command can be used to generate a new key."
          )
  
      if "unix_socket" in server_kwargs:
          if server_kwargs["port"] != DEFAULT_SERVER_PORT:
              die("--port arg is not supported with a unix socket")
          invalid_args = ["address", "ssl_certfile", "ssl_keyfile"]
  
          if any(server_kwargs.get(x) for x in invalid_args):
              die(
                  f"{[*invalid_args, 'port']} args are not supported with a unix socket"
              )
  
      auth_module_path = settings.auth_module(
          getattr(args, "auth_module", None)
      )
      if auth_module_path:
          server_kwargs["auth_provider"] = AuthModule(auth_module_path)
      else:
          server_kwargs["auth_provider"] = NullAuth()
  
      server_kwargs["xsrf_cookies"] = settings.xsrf_cookies(
          getattr(args, "enable_xsrf_cookies", False)
      )
      server_kwargs["cookie_secret"] = settings.cookie_secret(
          getattr(args, "cookie_secret", None)
      )
      server_kwargs["use_index"] = not args.disable_index
      server_kwargs["redirect_root"] = not args.disable_index_redirect
      server_kwargs["autoreload"] = args.dev is not None
      server_kwargs["ico_path"] = settings.ico_path(
          getattr(args, "ico_path", None)
      )
  
      def find_autoreload_targets(app_path: str) -> None:
          path = os.path.abspath(app_path)
          if not os.path.isdir(path):
              return
  
          for path, _, files in os.walk(path):
              for name in files:
                  if (
                      fnmatch(name, "*.html")
                      or fnmatch(name, "*.css")
                      or fnmatch(name, "*.yaml")
                  ):
                      log.info("Watching: " + os.path.join(path, name))
                      watch(os.path.join(path, name))
  
      def add_optional_autoreload_files(file_list: list[str]) -> None:
          for filen in file_list:
              if os.path.isdir(filen):
                  log.warning("Cannot watch directory " + filen)
                  continue
              log.info("Watching: " + filen)
              watch(filen)
  
      if server_kwargs["autoreload"]:
          if len(applications.keys()) != 1:
              die("--dev can only support a single app.")
          if server_kwargs["num_procs"] != 1:
              log.info(
                  "Running in --dev mode. --num-procs is limited to 1."
              )
              server_kwargs["num_procs"] = 1
  
          find_autoreload_targets(args.files[0])
          add_optional_autoreload_files(args.dev)
  
      applications = self.customize_applications(args, applications)
      server_kwargs = self.customize_kwargs(args, server_kwargs)
  
      with report_server_init_errors(**server_kwargs):
          server = Server(applications, **server_kwargs)
  
          if args.show:
              # we have to defer opening in browser until we start up the server
              def show_callback() -> None:
                  for route in applications.keys():
                      server.show(route)
  
              server.io_loop.add_callback(show_callback)
  
          # Server may not have a port when bound to a unix socket
          if server.port:
              address_string = "localhost"
              if server.address is not None and server.address != "":
                  address_string = server.address
  
              if server_kwargs["ssl_certfile"] and (
                  server_kwargs["ssl_certfile"].endswith(".pem")
                  or server_kwargs["ssl_keyfile"]
              ):
                  protocol = "https"
              else:
                  protocol = "http"
  
              for route in sorted(applications.keys()):
                  url = f"{protocol}://{address_string}:{server.port}{server.prefix}{route}"
                  log.info("Bokeh app running at: %s" % url)
  
              log.info(
                  "Starting Bokeh server with process id: %d"
                  % os.getpid()
              )
  
          server = self.customize_server(server)
  
          server.run_until_shutdown()
  
imports: from __future__ import annotations
  import logging
  import argparse
  import os
  from fnmatch import fnmatch
  from glob import glob
  from typing import TYPE_CHECKING, Any
  from tornado.autoreload import watch
  from bokeh.application import Application
  from bokeh.resources import DEFAULT_SERVER_PORT
  from bokeh.server.auth_provider import AuthModule, NullAuth
  from bokeh.server.tornado import DEFAULT_SESSION_TOKEN_EXPIRATION, DEFAULT_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES
  from bokeh.settings import settings
  from bokeh.util.logconfig import basicConfig
  from bokeh.util.strings import format_docstring, nice_join
  from ..subcommand import Argument, Subcommand
  from ..util import build_single_handler_applications, die, report_server_init_errors
label_desc: Use image node_id 4 for calling the Serve obj's underlying member method code with example usage: obj.invoke(args) without return types