def writer_thread(stream, queue):
    try:
        while True:
            to_write = queue.get()
            to_json = getattr(to_write, 'to_json', None)
            if to_json is not None:
                # Some protocol message
                to_write.seq = _next_seq()
                try:
                    to_write = to_json()
                except:
                    debug_exception('Error serializing %s to json.' %
                                    (to_write, ))
                    continue

            if DEBUG:
                debug('Writing: %s\n' % (to_write, ))

            if to_write.__class__ == bytes:
                as_bytes = to_write
            else:
                as_bytes = to_write.encode('utf-8')

            stream.write('Content-Length: %s\r\n\r\n' % (len(as_bytes)))
            stream.write(as_bytes)
            stream.flush()
    except:
        debug_exception()
Example #2
0
def _notify_on_exited(process, on_exited):
    try:
        process.wait()
        if DEBUG:
            debug('notify process exited\n')
        on_exited()
    except:
        debug_exception()
def main():
    '''
    Starts the debug adapter (creates a thread to read from stdin and another to write to stdout as 
    expected by the vscode debug protocol).
    
    We pass the command processor to the reader thread as the idea is that the reader thread will
    read a message, convert it to an instance of the message in the schema and then forward it to
    the command processor which will interpret and act on it, posting the results to the writer queue.
    '''
    try:
        import sys
        try:
            from queue import Queue
        except ImportError:
            from Queue import Queue

        write_queue = Queue()
        command_processor = CommandProcessor(write_queue)

        if DEBUG:
            debug('Starting. Args: %s\n' % (', '.join(sys.argv), ))

        write_to = sys.stdout
        read_from = sys.stdin

        if sys.version_info[0] <= 2:
            if sys.platform == "win32":
                # must read streams as binary on windows
                import os, msvcrt
                msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
        else:
            # Py3
            write_to = sys.stdout.buffer
            read_from = sys.stdin.buffer

        writer = threading.Thread(target=writer_thread,
                                  args=(write_to, write_queue))
        reader = threading.Thread(target=reader_thread,
                                  args=(read_from, command_processor))

        reader.start()
        writer.start()

        reader.join()
        writer.join()
    except:
        debug_exception()

    debug('exiting main.\n')
Example #4
0
 def __init__(self, request, launch_response, command_processor):
     '''
     :param LaunchRequest request:
     :param LaunchResponse launch_response:
     '''
     from debug_adapter._constants import VALID_CONSOLE_OPTIONS
     import weakref
     
     self._weak_command_processor = weakref.ref(command_processor)
     self._valid = True
     self._cmdline = []
     self._popen = None
     
     import sys
     import os.path
     
     file_to_run = request.arguments.kwargs.get('program')
     self._cwd = request.arguments.kwargs.get('cwd')
     self._console = request.arguments.kwargs.get('console')
     self._run_in_debug_mode = not request.arguments.noDebug
     
     if self._console not in VALID_CONSOLE_OPTIONS:
         launch_response.success = False
         launch_response.message = 'Invalid console option: %s (must be one of: %s)' % (
             self._console, VALID_CONSOLE_OPTIONS) 
         return
     
     if not os.path.exists(self._cwd):
         launch_response.success = False
         launch_response.message = 'cwd specified does not exist: %s' % (self._cwd,) 
         return
         
     if not os.path.exists(file_to_run):
         launch_response.success = False
         launch_response.message = 'File: %s does not exist.' % (file_to_run,)
         self._valid = False
         return
     
     # TODO: Properly handle debug/no debug mode
     if DEBUG:
         debug('Run in debug mode: %s\n' % (self._run_in_debug_mode,))
         
     cmdline = [sys.executable, '-u', file_to_run]
     self._cmdline = cmdline
Example #5
0
 def send_to_stdin(self, expression):
     popen = self._popen
     if popen is not None:
         import threading
         try:
             debug('Sending: %s to stdin.' % (expression,))
             def write_to_stdin(popen, expression):
                 popen.stdin.write(expression)
                 if not expression.endswith('\r') and not expression.endswith('\n'):
                     popen.stdin.write('\n')
                 popen.stdin.flush()
                 
             # Do it in a thread (in theory the OS could have that filled up and we would never complete
             # trying to write -- although probably a bit far fetched, let's code as if that could actually happen).
             t = threading.Thread(target=write_to_stdin, args=(popen, expression))
             t.setDaemon(True)
             t.start()
         except:
             debug_exception('Error writing: >>%s<< to stdin.' % (expression,))
    def __call__(self, protocol_message):
        if DEBUG:
            debug('Process json: %s\n' %
                  (json.dumps(protocol_message.to_dict(),
                              indent=4,
                              encoding='utf-8',
                              sort_keys=True), ))

        try:
            if protocol_message.type == 'request':
                method_name = 'on_%s_request' % (protocol_message.command, )
                on_request = getattr(self, method_name, None)
                if on_request is not None:
                    on_request(protocol_message)
                else:
                    if DEBUG:
                        debug(
                            'Unhandled: %s not available in CommandProcessor.\n'
                            % (method_name, ))
        except:
            debug_exception()
def read(stream):
    '''
    Reads one message from the stream and returns the related dict (or None if EOF was reached).
    
    :param stream:
        The stream we should be reading from.
        
    :return dict|NoneType:
        The dict which represents a message or None if the stream was closed.
    '''
    headers = {}
    while True:
        # Interpret the http protocol headers
        line = stream.readline()  # The trailing \r\n should be there.

        if DEBUG:
            debug(
                'read line: >>%s<<\n' %
                (line.replace('\r', '\\r').replace('\n', '\\n')), )

        if not line:  # EOF
            return None
        line = line.strip().decode('ascii')
        if not line:  # Read just a new line without any contents
            break
        try:
            name, value = line.split(': ', 1)
        except ValueError:
            raise RuntimeError('invalid header line: {}'.format(line))
        headers[name] = value

    if not headers:
        raise RuntimeError('got message without headers')

    size = int(headers['Content-Length'])

    # Get the actual json
    body = stream.read(size)

    return json.loads(body.decode('utf-8'))
Example #8
0
    def launch(self):
        from debug_adapter import schema
        from debug_adapter._constants import CONSOLE_EXTERNAL
        from debug_adapter._constants import CONSOLE_INTEGRATED
        from debug_adapter._constants import CONSOLE_NONE
        import threading
        
        # Note: using a weak-reference so that callbacks don't keep it alive            
        weak_command_processor = self._weak_command_processor
        
        console = self._console
        if not weak_command_processor().supports_run_in_terminal:
            # If the client doesn't support running in the terminal we fallback to using the debug console.
            console = CONSOLE_NONE
        
        def on_exited():
            command_processor = weak_command_processor()
            if command_processor is not None:
                restart = False
                terminated_event = schema.TerminatedEvent(body=schema.TerminatedEventBody(restart=restart))
                command_processor.write_message(terminated_event)
                
        threads = []
        if console == CONSOLE_NONE:
            import subprocess
            
            if DEBUG:
                debug('Launching in "none" console: %s' % (self._cmdline,))
                
            self._popen = subprocess.Popen(self._cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, cwd=self._cwd)

            def on_output(output, category):
                command_processor = weak_command_processor()
                if command_processor is not None:
                    output_event = schema.OutputEvent(schema.OutputEventBody(output, category=category))
                    command_processor.write_message(output_event)
            
            threads.append(threading.Thread(target=_read_stream, args=(self._popen.stdout, on_output, 'stdout')))
            threads.append(threading.Thread(target=_read_stream, args=(self._popen.stderr, on_output, 'stderr')))
            threads.append(threading.Thread(target=_notify_on_exited, args=(self._popen, on_exited)))
            
        elif console in (CONSOLE_INTEGRATED, CONSOLE_EXTERNAL):
            kind = 'external'
            if console == CONSOLE_INTEGRATED:
                kind = 'internal'
            
            if DEBUG:
                debug('Launching in "%s" console: %s' % (kind, self._cmdline,))
                
            command_processor = weak_command_processor()
            if command_processor is not None:
                # TODO: Provide an env
                command_processor.write_message(schema.RunInTerminalRequest(schema.RunInTerminalRequestArguments(
                    cwd=self._cwd, args=self._cmdline, kind=kind)))
                
                # When the user runs in the integrated terminal or in the external terminal, in regular run mode (i.e.:
                # no debug) , say that this session has been finished (he can close the process from the related
                # terminal).
                on_exited()
        
        for t in threads:
            t.setDaemon(True)
            
        for t in threads:
            t.start()