def test_write(self): data = io.BytesIO() stream = JsonIOStream(data, data) for message in self.MESSAGES: stream.write_json(message) data = data.getvalue() assert data == self.SERIALIZED_MESSAGES
def test_read(self): data = io.BytesIO(self.SERIALIZED_MESSAGES) stream = JsonIOStream(data, data) for expected_message in self.MESSAGES: message = stream.read_json() assert message == expected_message with pytest.raises(EOFError): stream.read_json()
def _setup_channel(self): self.stream = LoggingJsonStream(JsonIOStream.from_socket(self.socket), 'ptvsd#%d' % self.ptvsd_port) handlers = MessageHandlers(request=self._process_request, event=self._process_event) self.channel = JsonMessageChannel(self.stream, handlers) self.channel.start() self.connected.set()
def _subprocess_listener(): counter = itertools.count(1) while subprocess_listener_socket: try: (sock, _) = subprocess_listener_socket.accept() except Exception: break stream = JsonIOStream.from_socket(sock) _handle_subprocess(next(counter), stream)
def prepare_to_run(self, perform_handshake=True, filename=None, module=None): """Spawns ptvsd using the configured method, telling it to execute the provided Python file or module, and establishes a message channel to it. If perform_handshake is True, calls self.handshake() before returning. """ argv = [sys.executable] if self.method != 'attach_pid': argv += ['-m', 'ptvsd'] if self.method == 'attach_socket': if self.ptvsd_port is None: self.ptvsd_port = 5678 argv += ['--port', str(self.ptvsd_port)] else: port = self._listen() argv += ['--host', 'localhost', '--port', str(port)] if filename: assert not module argv += [filename] elif module: assert not filename argv += ['-m', module] env = os.environ.copy() env.update({'PYTHONPATH': PTVSD_SYS_PATH}) print('Spawning %r' % argv) self.process = subprocess.Popen(argv, env=env, stdin=subprocess.PIPE) if self.ptvsd_port: # ptvsd will take some time to spawn and start listening on the port, # so just hammer at it until it responds (or we time out). while not self.socket: try: self._connect() except socket.error: pass time.sleep(0.1) else: self.connected.wait() assert self.socket self.stream = LoggingJsonStream(JsonIOStream.from_socket(self.socket)) handlers = MessageHandlers(request=self._process_request, event=self._process_event) self.channel = JsonMessageChannel(self.stream, handlers) self.channel.start() if perform_handshake: self.handshake()
def _backchannel_worker(self): print('Listening for incoming backchannel connection for bchan#%d' % self.ptvsd_port) sock = None try: sock, _ = self.backchannel_socket.accept() except socket.timeout: assert sock is not None, 'bchan#%r timed out!' % self.ptvsd_port print('Incoming bchan#%d backchannel connection accepted' % self.ptvsd_port) sock.settimeout(None) self._backchannel_stream = LoggingJsonStream(JsonIOStream.from_socket(sock), 'bchan#%d' % self.ptvsd_port) self.backchannel_established.set()
def _subprocess_listener(): counter = itertools.count(1) while subprocess_listener_socket: try: (sock, _) = subprocess_listener_socket.accept() except Exception: break n = next(counter) name = 'subprocess-{}'.format(n) ptvsd.log.debug('Accepted incoming connection from {0}', name) stream = JsonIOStream.from_socket(sock, name=name) _handle_subprocess(n, stream)
def notify_root(port): assert options.subprocess_of debug('Subprocess %d notifying root process at port %d' % (os.getpid(), options.subprocess_notify)) conn = create_client() conn.connect(('localhost', options.subprocess_notify)) stream = JsonIOStream.from_socket(conn) channel = JsonMessageChannel(stream) channel.start() # Send the notification about ourselves to root, and wait for it to tell us # whether an incoming connection is anticipated. This will be true if root # had successfully propagated the notification to the IDE, and false if it # couldn't do so (e.g. because the IDE is not attached). There's also the # possibility that connection to root will just drop, e.g. if it crashes - # in that case, just exit immediately. request = channel.send_request( 'ptvsd_subprocess', { 'parentProcessId': options.subprocess_of, 'processId': os.getpid(), 'port': port, }) try: response = request.wait_for_response() except Exception: print('Failed to send subprocess notification; exiting', file=sys.__stderr__) traceback.print_exc() sys.exit(0) # Keep the channel open until we exit - root process uses open channels to keep # track of which subprocesses are alive and which are not. atexit.register(lambda: channel.close()) if not response['incomingConnection']: debugger = get_global_debugger() while debugger is None: time.sleep(0.1) debugger = get_global_debugger() debugger.ready_to_run = True
def notify_root(port): assert options.subprocess_of debug('Subprocess %d notifying root process at port %d' % (os.getpid(), options.subprocess_notify)) conn = create_client() conn.connect(('localhost', options.subprocess_notify)) stream = JsonIOStream.from_socket(conn) channel = JsonMessageChannel(stream) channel.start() # Send the notification about ourselves to root, and wait for it to tell us # whether an incoming connection is anticipated. This will be true if root # had successfully propagated the notification to the IDE, and false if it # couldn't do so (e.g. because the IDE is not attached). There's also the # possibility that connection to root will just drop, e.g. if it crashes - # in that case, just exit immediately. request = channel.send_request('ptvsd_subprocess', { 'parentProcessId': options.subprocess_of, 'processId': os.getpid(), 'port': port, }) try: response = request.wait_for_response() except Exception: print('Failed to send subprocess notification; exiting', file=sys.__stderr__) traceback.print_exc() sys.exit(0) # Keep the channel open until we exit - root process uses open channels to keep # track of which subprocesses are alive and which are not. atexit.register(lambda: channel.close()) if not response['incomingConnection']: debugger = get_global_debugger() while debugger is None: time.sleep(0.1) debugger = get_global_debugger() debugger.ready_to_run = True
def test_fuzz(self): # Set up two channels over the same stream that send messages to each other # asynchronously, and record everything that they send and receive. # All records should match at the end. class Fuzzer(object): def __init__(self, name): self.name = name self.lock = threading.Lock() self.sent = [] self.received = [] self.responses_sent = [] self.responses_received = [] def start(self, channel): self._worker = threading.Thread(name=self.name, target=lambda: self._send_requests_and_events(channel)) self._worker.daemon = True self._worker.start() def wait(self): self._worker.join() def fizz_event(self, channel, body): with self.lock: self.received.append(('event', 'fizz', body)) def buzz_event(self, channel, body): with self.lock: self.received.append(('event', 'buzz', body)) def event(self, channel, event, body): with self.lock: self.received.append(('event', event, body)) def make_and_log_response(self, command): x = random.randint(-100, 100) if x >= 0: response = Response(True, command, body=x) else: response = Response(False, command, error_message=str(x)) with self.lock: self.responses_sent.append(response) if response.success: return x else: raise RuntimeError(response.error_message) def fizz_request(self, channel, arguments): with self.lock: self.received.append(('request', 'fizz', arguments)) return self.make_and_log_response('fizz') def buzz_request(self, channel, arguments): with self.lock: self.received.append(('request', 'buzz', arguments)) return self.make_and_log_response('buzz') def request(self, channel, command, arguments): with self.lock: self.received.append(('request', command, arguments)) return self.make_and_log_response(command) def _send_requests_and_events(self, channel): pending_requests = [0] for _ in range(0, 100): typ = random.choice(('event', 'request')) name = random.choice(('fizz', 'buzz', 'fizzbuzz')) body = random.randint(0, 100) with self.lock: self.sent.append((typ, name, body)) if typ == 'event': channel.send_event(name, body) elif typ == 'request': with self.lock: pending_requests[0] += 1 req = channel.send_request(name, body) def response_handler(response): with self.lock: self.responses_received.append(response) pending_requests[0] -= 1 req.on_response(response_handler) # Spin until we get responses to all requests. while True: with self.lock: if pending_requests[0] == 0: break time.sleep(0.1) fuzzer1 = Fuzzer('fuzzer1') fuzzer2 = Fuzzer('fuzzer2') server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 0)) _, port = server_socket.getsockname() server_socket.listen(0) socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket1_thread = threading.Thread(target=lambda: socket1.connect(('localhost', port))) socket1_thread.start() socket2, _ = server_socket.accept() socket1_thread.join() try: io1 = socket1.makefile('rwb', 0) io2 = socket2.makefile('rwb', 0) stream1 = LoggingJsonStream(JsonIOStream(io1, io1)) channel1 = JsonMessageChannel(stream1, fuzzer1) channel1.start() fuzzer1.start(channel1) stream2 = LoggingJsonStream(JsonIOStream(io2, io2)) channel2 = JsonMessageChannel(stream2, fuzzer2) channel2.start() fuzzer2.start(channel2) fuzzer1.wait() fuzzer2.wait() finally: socket1.close() socket2.close() assert fuzzer1.sent == fuzzer2.received assert fuzzer2.sent == fuzzer1.received assert fuzzer1.responses_sent == fuzzer2.responses_received assert fuzzer2.responses_sent == fuzzer1.responses_received
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See LICENSE in the project root # for license information. from __future__ import print_function, with_statement, absolute_import """Imported from test code that runs under ptvsd, and allows that code to communcate back to the test. Works in conjunction with debug_session fixture and its backchannel method.""" import os import socket from ptvsd.messaging import JsonIOStream port = int(os.getenv('PTVSD_BACKCHANNEL_PORT')) # print('Connecting to bchan#%d' % port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', port)) stream = JsonIOStream.from_socket(sock) def read_json(): return stream.read_json() def write_json(value): stream.write_json(value)
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See LICENSE in the project root # for license information. from __future__ import print_function, with_statement, absolute_import """Imported from test code that runs under ptvsd, and allows that code to communcate back to the test. Works in conjunction with debug_session fixture and its backchannel method.""" import os import socket import ptvsd.log from ptvsd.messaging import JsonIOStream port = int(os.getenv('PTVSD_BACKCHANNEL_PORT')) ptvsd.log.debug('Connecting to bchan#{0}', port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', port)) stream = JsonIOStream.from_socket(sock, name='test') def read_json(): return stream.read_json() def write_json(value): stream.write_json(value)