code: def on_state_change(
      settings,
      tabpy_state,
      python_service,
      logger=logging.getLogger(__name__),
  ):
      try:
          logger.log(logging.INFO, "Loading state from state file")
          config = util._get_state_from_file(
              settings[SettingsParameters.StateFilePath], logger=logger
          )
          new_ps_state = TabPyState(config=config, settings=settings)
  
          (has_changes, changes) = _get_latest_service_state(
              settings, tabpy_state, new_ps_state, python_service
          )
          if not has_changes:
              logger.info("Nothing changed, return.")
              return
  
          new_endpoints = new_ps_state.get_endpoints()
          for object_name in changes["endpoints"]:
              (object_type, object_version, object_path) = changes[
                  "endpoints"
              ][object_name]
  
              if not object_path and not object_version:  # removal
                  logger.info(f"Removing object: URI={object_name}")
  
                  python_service.manage_request(
                      DeleteObjects([object_name])
                  )
  
                  cleanup_endpoint_files(
                      object_name,
                      settings[SettingsParameters.UploadDir],
                      logger=logger,
                  )
  
              else:
                  endpoint_info = new_endpoints[object_name]
                  is_update = object_version > 1
                  if object_type == "alias":
                      msg = LoadObject(
                          object_name,
                          endpoint_info["target"],
                          object_version,
                          is_update,
                          "alias",
                      )
                  else:
                      local_path = object_path
                      msg = LoadObject(
                          object_name,
                          local_path,
                          object_version,
                          is_update,
                          object_type,
                      )
  
                  python_service.manage_request(msg)
                  wait_for_endpoint_loaded(python_service, object_name)
  
                  # cleanup old version of endpoint files
                  if object_version > 2:
                      cleanup_endpoint_files(
                          object_name,
                          settings[SettingsParameters.UploadDir],
                          logger=logger,
                          retain_versions=[
                              object_version,
                              object_version - 1,
                          ],
                      )
  
      except Exception as e:
          err_msg = format_exception(e, "on_state_change")
          logger.log(
              logging.ERROR,
              f"Error submitting update model request: error={err_msg}",
          )
  
file_path: TabPy/tabpy/tabpy_server/psws/callbacks.py
imports: import logging
  from tabpy.tabpy_server.app.app_parameters import SettingsParameters
  from tabpy.tabpy_server.common.messages import LoadObject, DeleteObjects, ListObjects, ObjectList
  from tabpy.tabpy_server.common.endpoint_file_mgr import cleanup_endpoint_files
  from tabpy.tabpy_server.common.util import format_exception
  from tabpy.tabpy_server.management.state import TabPyState, get_query_object_path
  from tabpy.tabpy_server.management import util
  from time import sleep
  from tornado import gen
label_desc: Use image node_id 5 for calling a global function with example usage: on_state_change(settings, tabpy_state, python_service, logger) without return types