docstr: Process the message through the Perspective engine, handling the
  special flow for binary messages along the way.
docstr_tok: ["Process","the","message","through","the","Perspective","engine",",","handling","the","special","flow","for","binary","messages","along","the","way","."]
code: def __process(self, msg, post_callback, client_id=None):
      """Process the message through the Perspective engine, handling the
      special flow for binary messages along the way."""
      if len(self._pending_binary) >= 0 and isinstance(msg, bytes):
          # The message is an ArrayBuffer, and it needs to be combined with
          # the previous message to reconsitute metadata before processing.
          full_message = self._pending_binary.popleft()
          full_message.pop("binary_length")
  
          # msg is the binary
          new_args = [msg]
  
          if len(full_message["args"]) > 1:
              # append additional args, such as port_id. Javascript will
              # serialize the ArrayBuffer as an empty object, so ignore
              # the first argument always.
              new_args += full_message["args"][1:]
  
          full_message["args"] = new_args
          msg = full_message
  
      if isinstance(msg, str):
          msg = json.loads(msg)
  
      if not isinstance(msg, dict):
          raise PerspectiveError(
              "Message passed into `_process` should either be a "
              "JSON-serialized string or a dict."
          )
  
      if msg.get("binary_length", None):
          # cache the message and wait for the ArrayBuffer that will
          # follow immediately after.
          self._pending_binary.append(msg)
          return
  
      cmd = msg["cmd"]
  
      if self._is_locked_command(msg) is True:
          error_string = "`{0}` failed - access denied".format(
              msg["cmd"]
              + (
                  ("." + msg["method"])
                  if msg.get("method", None) is not None
                  else ""
              )
          )
          error_message = self._make_error_message(
              msg["id"], error_string
          )
          post_callback(self._message_to_json(msg["id"], error_message))
          return
  
      try:
          if cmd == "init":
              # The client should wait for the return message on table()
              # and view() to confirm successful creation.
              flags = ["wait_for_response"]
  
              # Return a message to the client to confirm initialization with
              # a list of feature flags that the client will use to enable
              # or disable behavior depending on its version.
              message = self._make_message(msg["id"], flags)
              post_callback(self._message_to_json(msg["id"], message))
          elif cmd == "get_hosted_table_names":
              message = self._make_message(
                  msg["id"], [k for k in self._tables.keys()]
              )
              post_callback(self._message_to_json(msg["id"], message))
          elif cmd == "table":
              try:
                  # create a new Table and track it
                  data_or_schema = msg["args"][0]
                  self._tables[msg["name"]] = Table(
                      data_or_schema, **msg.get("options", {})
                  )
  
                  # Return the Table's name to the front end so it can be
                  # resolved.
                  message = self._make_message(msg["id"], msg["name"])
                  post_callback(
                      self._message_to_json(msg["id"], message)
                  )
              except IndexError:
                  self._tables[msg["name"]] = []
          elif cmd == "view":
              # Create a new view and notify the client the view has been
              # created by sending the view name back.
              new_view = self._tables[msg["table_name"]].view(
                  **msg.get("config", {})
              )
              new_view._client_id = client_id
              self._views[msg["view_name"]] = new_view
  
              # Return the View's name to the front end so it can be
              # resolved.
              message = self._make_message(msg["id"], msg["view_name"])
              post_callback(self._message_to_json(msg["id"], message))
          elif cmd == "table_method" or cmd == "view_method":
              # Call the method on the table/view instance
              self._process_method_call(msg, post_callback, client_id)
          else:
              logging.error("Unknown client message " + str(msg))
      except (PerspectiveError, PerspectiveCppError) as error:
          # Log errors and return them to the client
          error_string = str(error)
          error_message = self._make_error_message(
              msg["id"], error_string
          )
          logging.error(
              "[PerspectiveManager._process] %s", error_string
          )
          post_callback(self._message_to_json(msg["id"], error_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 4 for calling the _PerspectiveManagerInternal obj's underlying member method code with example usage: obj.__process(msg, post_callback, client_id) without return types