code: def test_session_close_multiple_sessions_with_callbacks(
      self, sentinel
  ):
      s = sentinel(0)
  
      manager = PerspectiveManager()
      sessions = [manager.new_session() for i in range(5)]
  
      # create a table and view using manager
      make_table = {
          "id": 1,
          "name": "table1",
          "cmd": "table",
          "args": [data],
      }
      manager._process(make_table, self.post)
  
      # create a view on each session
      for i, session in enumerate(sessions):
          # IDs have to conflict - each viewer will send the first message as
          # ID = 1, so we need to make sure we handle that.
          msg = {
              "id": 2,
              "table_name": "table1",
              "view_name": "view" + str(i),
              "cmd": "view",
          }
          session.process(msg, self.post)
  
      manager_views = list(manager._views.keys())
      for key in ["view" + str(i) for i in range(5)]:
          assert key in manager_views
  
      for i, session in enumerate(sessions):
          view = manager._get_view("view" + str(i))
          assert view._client_id == session.client_id
  
      def callback(updated):
          assert updated["port_id"] == 0
          s.set(s.get() + 100)
  
      # simulate a client that holds callbacks by id
      callbacks = {3: callback}
  
      def post_update(msg):
          # when `on_update` is triggered, this callback gets the message
          # and has to decide which callback to trigger.
          message = json.loads(msg)
          assert message["id"] is not None
          if message["id"] == 3:
              # trigger callback
              assert message["data"] == {"port_id": 0}
              callbacks[message["id"]](message["data"])
  
      # create a view and an on_update on each session
      for i, session in enumerate(sessions):
          view_name = "view" + str(i)
          # IDs have to conflict - each viewer will send the first message as
          # ID = 1, so we need to make sure we handle that.
          msg = {
              "id": 2,
              "table_name": "table1",
              "view_name": view_name,
              "cmd": "view",
          }
          session.process(msg, self.post)
          make_on_update = {
              "id": 3,
              "name": view_name,
              "cmd": "view_method",
              "subscribe": True,
              "method": "on_update",
              "callback_id": "callback_1",
          }
          session.process(make_on_update, post_update)
  
      # call updates using a random session - they should propagate
      random_session_id = random.randint(0, 4)
      random_session = sessions[random_session_id]
      random_client_id = random_session.client_id
  
      update1 = {
          "id": 4,
          "name": "table1",
          "cmd": "table_method",
          "method": "update",
          "args": [{"a": [4], "b": ["d"]}],
      }
      update2 = {
          "id": 5,
          "name": "table1",
          "cmd": "table_method",
          "method": "update",
          "args": [{"a": [5], "b": ["e"]}],
      }
  
      random_session.process(update1, self.post)
      random_session.process(update2, self.post)
  
      # all updates processed, all callbacks fired
      assert s.get() == 1000
  
      # close a random session, and make sure the other views and callbacks
      # are not affected
      random_session.close()
  
      # make sure the view is gone - but not the table
      assert "table1" in manager._tables
  
      assert (
          "view" + str(random_session_id) not in manager._views.keys()
      )
      assert len(manager._views.keys()) == 4
  
      for callback in manager._callback_cache:
          assert callback["client_id"] != random_client_id
  
      assert len(manager._callback_cache) == 4
  
file_path: perspective/python/perspective/perspective/tests/manager/test_session.py
imports: import json
  import random
  from perspective import Table, PerspectiveManager
label_desc: Use image node_id 6 for calling the TestPerspectiveSession obj's underlying member method code with example usage: obj.test_session_close_multiple_sessions_with_callbacks(sentinel) without return types