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)