docstr: Add or update an endpoint
docstr_tok: ["Add","or","update","an","endpoint"]
code: def _add_or_update_endpoint(
      self, action, name, version, request_data
  ):
      """
      Add or update an endpoint
      """
      self.logger.log(logging.DEBUG, f"Adding/updating model {name}...")
  
      if not isinstance(name, str):
          msg = "Endpoint name must be a string"
          self.logger.log(logging.CRITICAL, msg)
          raise TypeError(msg)
  
      name_checker = _compile(r"^[a-zA-Z0-9-_\s]+$")
      if not name_checker.match(name):
          raise gen.Return(
              "endpoint name can only contain: a-z, A-Z, 0-9,"
              " underscore, hyphens and spaces."
          )
  
      if self.settings.get("add_or_updating_endpoint"):
          msg = (
              "Another endpoint update is already in progress"
              ", please wait a while and try again"
          )
          self.logger.log(logging.CRITICAL, msg)
          raise RuntimeError(msg)
  
      self.settings["add_or_updating_endpoint"] = random_uuid()
      try:
          docstring = None
          if "docstring" in request_data:
              docstring = str(
                  bytes(request_data["docstring"], "utf-8").decode(
                      "unicode_escape"
                  )
              )
  
          description = request_data.get("description", None)
          endpoint_type = request_data.get("type", None)
          methods = request_data.get("methods", [])
          dependencies = request_data.get("dependencies", None)
          target = request_data.get("target", None)
          schema = request_data.get("schema", None)
          src_path = request_data.get("src_path", None)
          target_path = get_query_object_path(
              self.settings[SettingsParameters.StateFilePath],
              name,
              version,
          )
  
          path_checker = _compile(r"^[\\\:a-zA-Z0-9-_~\s/\.\(\)]+$")
          # copy from staging
          if src_path:
              if not isinstance(src_path, str):
                  raise gen.Return("src_path must be a string.")
              if not path_checker.match(src_path):
                  raise gen.Return(
                      f"Invalid source path for endpoint {name}"
                  )
  
              yield self._copy_po_future(src_path, target_path)
          elif endpoint_type != "alias":
              raise gen.Return(
                  "src_path is required to add/update an endpoint."
              )
          else:
              # alias special logic:
              if not target:
                  raise gen.Return(
                      "Target is required for alias endpoint."
                  )
              dependencies = [target]
  
          # update local config
          try:
              if action == "add":
                  self.tabpy_state.add_endpoint(
                      name=name,
                      description=description,
                      docstring=docstring,
                      endpoint_type=endpoint_type,
                      methods=methods,
                      dependencies=dependencies,
                      target=target,
                      schema=schema,
                  )
              else:
                  self.tabpy_state.update_endpoint(
                      name=name,
                      description=description,
                      docstring=docstring,
                      endpoint_type=endpoint_type,
                      methods=methods,
                      dependencies=dependencies,
                      target=target,
                      schema=schema,
                      version=version,
                  )
  
          except Exception as e:
              raise gen.Return(f"Error when changing TabPy state: {e}")
  
          on_state_change(
              self.settings,
              self.tabpy_state,
              self.python_service,
              self.logger,
          )
  
      finally:
          self.settings["add_or_updating_endpoint"] = None
  
file_path: TabPy/tabpy/tabpy_server/handlers/management_handler.py
imports: import logging
  import os
  import shutil
  from re import compile
  from uuid import uuid4
  from tornado import gen
  from tabpy.tabpy_server.app.app_parameters import SettingsParameters
  from tabpy.tabpy_server.handlers import BaseHandler
  from tabpy.tabpy_server.handlers.base_handler import STAGING_THREAD
  from tabpy.tabpy_server.management.state import get_query_object_path
  from tabpy.tabpy_server.psws.callbacks import on_state_change
label_desc: Use image node_id 3 for calling the ManagementHandler obj's underlying member method code with example usage: obj._add_or_update_endpoint(action, name, version, request_data) without return types