docstr: When the client calls a method, validate the instance it calls on
  and return the result.
docstr_tok: ["When","the","client","calls","a","method",",","validate","the","instance","it","calls","on","and","return","the","result","."]
code: def _process_method_call(self, msg, post_callback, client_id):
      """When the client calls a method, validate the instance it calls on
      and return the result.
      """
      if msg["cmd"] == "table_method":
          table_or_view = self._tables.get(msg["name"], None)
      else:
          table_or_view = self._views.get(msg["name"], None)
          if table_or_view is None:
              error_message = self._make_error_message(
                  msg["id"], "View method cancelled"
              )
              post_callback(
                  self._message_to_json(msg["id"], error_message)
              )
      try:
          if msg.get("subscribe", False) is True:
              self._process_subscribe(
                  msg, table_or_view, post_callback, client_id
              )
          else:
              # Decide how to dispatch the method
              arguments = {}
  
              if msg["method"] in (
                  "schema",
                  "expression_schema",
                  "validate_expressions",
              ):
                  # make sure schema returns string types through the
                  # wire API. `as_string` is respected by both the table
                  # and view's `schema` and `expression_schema` methods.
                  arguments["as_string"] = True
              elif msg["method"].startswith("to_"):
                  # parse options in `to_format` calls
                  for d in msg.get("args", []):
                      arguments.update(d)
              else:
                  # Otherwise, arguments are always passed as arrays of
                  # individual arguments.
                  arguments = msg.get("args", [])
  
              if msg["method"] == "delete":
                  if msg["cmd"] == "view_method":
                      # views can be removed, but tables cannot - intercept
                      # calls to `delete` on the view and return.
                      self._views[msg["name"]].delete()
                      self._views.pop(msg["name"], None)
                      return
                  else:
                      # Return an error when `table.delete()` is called
                      # over the wire API.
                      raise PerspectiveError(
                          "table.delete() cannot be called on a remote table, as the remote has full ownership."
                      )
  
              # Dispatch the method using the expected argument form
              if msg["method"].startswith("to_"):
                  # to_format takes dictionary of options
                  result = getattr(table_or_view, msg["method"])(
                      **arguments
                  )
              elif msg["method"] in ("update", "remove"):
                  # Apply first arg as positional, then options dict as kwargs
                  data = arguments[0]
                  options = {}
                  if len(arguments) > 1 and isinstance(
                      arguments[1], dict
                  ):
                      options = arguments[1]
                  result = getattr(table_or_view, msg["method"])(
                      data, **options
                  )
              elif (
                  msg["cmd"] == "table_method"
                  and msg["method"] == "validate_expressions"
              ):
                  # validate_expressions on the table takes `as_string` in
                  # wargs,
                  result = getattr(table_or_view, msg["method"])(
                      *msg.get("args", []), **arguments
                  )
              else:
                  # otherwise parse arguments as list
                  result = getattr(table_or_view, msg["method"])(
                      *arguments
                  )
  
              # result has been returned from Perspective, now deliver
              # it back to the user.
              if (
                  isinstance(result, bytes)
                  and msg["method"] != "to_csv"
              ):
                  # return a binary to the client without JSON serialization,
                  # i.e. when we return an Arrow. If a method is added that
                  # returns a string, this condition needs to be updated as
                  # an Arrow binary is both `str` and `bytes` in Python 2.
                  self._process_bytes(result, msg, post_callback)
              else:
                  # return the result to the client
                  message = self._make_message(msg["id"], result)
                  post_callback(
                      self._message_to_json(msg["id"], message)
                  )
      except Exception as error:
          error_string = str(error)
          message = self._make_error_message(msg["id"], error_string)
          logging.error(
              "[PerspectiveManager._process_method_call] %s",
              error_string,
          )
          post_callback(self._message_to_json(msg["id"], message))
  
imports: import datetime
  import json
  import logging
  from collections import deque
  from functools import partial
  from ..core.exception import PerspectiveError
  from ..table import PerspectiveCppError, Table
  from ..table._callback_cache import _PerspectiveCallBackCache
  from ..table._date_validator import _PerspectiveDateValidator
label_desc: Use image node_id 5 for calling the _PerspectiveManagerInternal obj's underlying member method code with example usage: obj._process_method_call(msg, post_callback, client_id) without return types