docs: def command_loop(self, run_script_event):
          """ This is the debugger command loop that processes (protocol) client
          requests.
          """
          while True:
              obj = remote_client.receive(self)
              command = obj["command"]  
              # TODO: ensure we always have a command if receive returns
              args = obj.get('args', {})
          
              if command == 'getBreakpoints':
                  breakpoints_list = IKBreakpoint.get_breakpoints_list()
                  remote_client.reply(obj, breakpoints_list)
                  _logger.b_debug("getBreakpoints(%s) => %s", args, breakpoints_list)
                  
              elif command == "setBreakpoint":
                  # Set a new breakpoint. If the lineno line doesn't exist for the
                  # filename passed as argument, return an error message.
                  # The filename should be in canonical form, as described in the 
                  # canonic() method.
                  file_name = args['file_name']
                  line_number = args['line_number']
                  condition = args.get('condition', None)
                  enabled = args.get('enabled', True)
                  _logger.b_debug("setBreakpoint(file_name=%s, line_number=%s,"
                                  " condition=%s, enabled=%s) with CWD=%s",
                                  file_name,
                                  line_number,
                                  condition,
                                  enabled,
                                  os.getcwd())
                  
                  error_messages = []
                  result = {}
  
                  c_file_name = self.normalize_path_in(file_name)
                  if not c_file_name:
                      err = "Failed to find file '%s'" % file_name
                      _logger.g_error("setBreakpoint error: %s", err)
                      msg = "IKP3db error: Failed to set a breakpoint at %s:%s "\
                            "(%s)." % (file_name, line_number, err)
                      error_messages = [msg]
                      command_exec_status = 'error'
                  else:                        
                      err, bp_number = self.set_breakpoint(c_file_name, 
                                                           line_number, 
                                                           condition=condition,
                                                           enabled=enabled)
                      if err:
                          _logger.g_error("setBreakpoint error: %s", err)
                          msg = "IKP3db error: Failed to set a breakpoint at %s:%s "\
                                "(%s)." % (file_name, line_number, err,)
                          error_messages = [msg]
                          command_exec_status = 'error'
                      else:
                          result = {'breakpoint_number': bp_number}
                          command_exec_status = 'ok'
                  remote_client.reply(obj, result, 
                                      command_exec_status=command_exec_status,
                                      error_messages=error_messages)
              
              elif command == "changeBreakpointState":
                  # Allows to:
                  #  - activate or deactivate breakpoint 
                  #  - set or remove condition
                  _logger.b_debug("changeBreakpointState(%s)", args)
                  bp_number = args.get('breakpoint_number', None)
                  if bp_number is None:
                      result = {}
                      msg = "changeBreakpointState() error: missing required " \
                            "breakpointNumber parameter."
                      _logger.g_error("    "+msg)
                      error_messages = [msg]
                      command_exec_status = 'error'
                  else:
                      err = self.change_breakpoint_state(bp_number,
                                                         args.get('enabled', False), 
                                                         condition=args.get('condition', ''))
                      result = {}
                      error_messages = []
                      if err:
                          msg = "changeBreakpointState() error: \"%s\"" % err
                          _logger.g_error("    "+msg)
                          error_messages = [msg]
                          command_exec_status = 'error'
                      else:
                          command_exec_status = 'ok'
                  remote_client.reply(obj, result, 
                                      command_exec_status=command_exec_status,
                                      error_messages=error_messages)
                  _logger.b_debug("    command_exec_status => %s", command_exec_status)
  
              elif command == "clearBreakpoint":
                  _logger.b_debug("clearBreakpoint(%s)", args)
                  bp_number = args.get('breakpoint_number', None)
                  if bp_number is None:
                      result = {}
                      msg = "IKP3db error: Failed to delete breakpoint (Missing "\
                            "required breakpointNumber parameter)."
                      error_messages = [msg]
                      command_exec_status = 'error'
                  else:
                      err = self.clear_breakpoint(args['breakpoint_number'])
                      result = {}
                      error_messages = []
                      if err:
                          msg = "IKP3db error: Failed to delete breakpoint (%s)." % err
                          _logger.g_error(msg)
                          error_messages = [msg]
                          command_exec_status = 'error'
                      else:
                          command_exec_status = 'ok'
                  remote_client.reply(obj, result, 
                                      command_exec_status=command_exec_status,
                                      error_messages=error_messages)
  
              elif command == 'runScript':
                  #TODO: handle a 'stopAtEntry' arg
                  _logger.x_debug("runScript(%s)", args)
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  run_script_event.set()
  
              elif command == 'suspend':
                  _logger.x_debug("suspend(%s)", args)
                  # We return a running status which is True at that point. Next 
                  # programBreak will change status to 'stopped'
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  self.setup_suspend()
                  
              elif command == 'resume':
                  _logger.x_debug("resume(%s)", args)
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  self._command_q.put({'cmd':'resume'})
  
              elif command == 'stepOver':  # <=> Pdb n(ext)
                  _logger.x_debug("stepOver(%s)", args)
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  self._command_q.put({'cmd':'stepOver'})
  
              elif command == 'stepInto':  # <=> Pdb s(tep)
                  _logger.x_debug("stepInto(%s)", args)
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  self._command_q.put({'cmd':'stepInto'})
  
              elif command == 'stepOut':  # <=> Pdb r(eturn)
                  _logger.x_debug("stepOut(%s)", args)
                  remote_client.reply(obj, {'executionStatus': 'running'})
                  self._command_q.put({'cmd':'stepOut'})
  
              elif command == 'evaluate':
                  _logger.e_debug("evaluate(%s)", args)
                  if self.tracing_enabled and self.status == 'stopped':
                      self._command_q.put({
                          'cmd':'evaluate',
                          'obj': obj,
                          'frame': args['frame'],
                          'expression': args['expression'],
                          'global': args['global'],
                          'disableBreak': args['disableBreak']
                      })
                      # reply will be done in _tracer() where result is available
                  else:
                      remote_client.reply(obj, {'value': None, 'type': None})
                      
              elif command == 'getProperties':
                  _logger.e_debug("getProperties(%s,%s)", args, obj)
                  if self.tracing_enabled and self.status == 'stopped':
                      if args.get('id'):
                          self._command_q.put({
                              'cmd':'getProperties',
                              'obj': obj,
                              'id': args['id']
                          })
                          # reply will be done in _tracer() when result is available
                      else:
                          result={}
                          command_exec_status = 'error'
                          error_messages = ["IKP3db received getProperties command sent without target variable 'id'."]
                          remote_client.reply(obj, result, 
                                              command_exec_status=command_exec_status,
                                              error_messages=error_messages)
  
                  else:
                      remote_client.reply(obj, {'value': None, 'type': None})
  
              elif command == 'setVariable':
                  _logger.e_debug("setVariable(%s)", args)
                  if self.tracing_enabled and self.status == 'stopped':
                      self._command_q.put({
                          'cmd':'setVariable',
                          'obj': obj,
                          'frame': args['frame'],
                          'name': args['name'],  # TODO: Rework plugin to send var's id
                          'value': args['value']
                      })
                      # reply will be done in _tracer() when result is available
                  else:
                      remote_client.reply(obj, {'value': None, 'type': None})
  
              elif command == 'reconnect':
                  _logger.n_debug("reconnect(%s)", args)
                  remote_client.reply(obj, {'executionStatus': self.status})
                  
              elif command == 'getThreads':
                  _logger.x_debug("getThreads(%s)", args)
                  threads_list = self.get_threads()
                  remote_client.reply(obj, threads_list)
          
              elif command == 'setDebuggedThread':
                  _logger.x_debug("setDebuggedThread(%s)", args)
                  ret_val = self.set_debugged_thread(args['ident'])
                  if ret_val['error']:
                      remote_client.reply(obj, 
                                          {},  # result
                                          command_exec_status='error',
                                          error_messages=[ret_val['error']])
  
                  else:
                      remote_client.reply(obj, ret_val['result'])
  
              elif command == '_InternalQuit':
                  # '_InternalQuit' is an IKP3db internal message, generated by 
                  # IKPdbConnectionHandler when a socket.error occured.
                  # Usually this occurs when socket has been destroyed as 
                  # debugged program sys.exit()
                  # So we leave the command loop to stop the debugger thread
                  # in order to allow debugged program to shutdown correctly.
                  # This message must NEVER be send by remote client.
                  _logger.e_debug("_InternalQuit(%s)", args)
                  self._command_q.put({'cmd':'_InternalQuit'})
                  return
              
              else: # unrecognized command ; just log and ignored
                  _logger.g_critical("Unsupported command '%s' ignored.", command)
  
              if IKPdbLogger.enabled:
                  _logger.b_debug("Current breakpoints list [any_active_breakpoint=%s]:", 
                                  IKBreakpoint.any_active_breakpoint) 
                  _logger.b_debug("    IKBreakpoint.breakpoints_by_file_and_line:")
                  if not IKBreakpoint.breakpoints_by_file_and_line:
                      _logger.b_debug("        <empty>") 
                  for file_line, bp in list(IKBreakpoint.breakpoints_by_file_and_line.items()):
                      _logger.b_debug("        %s => #%s, enabled=%s, condition=%s, %s", 
                                      file_line,
                                      bp.number,
                                      bp.enabled,
                                      repr(bp.condition),
                                      bp)
                  _logger.b_debug("    IKBreakpoint.breakpoints_files = %s", 
                                  IKBreakpoint.breakpoints_files)
                  _logger.b_debug("    IKBreakpoint.breakpoints_by_number = %s", 
                                  IKBreakpoint.breakpoints_by_number)