mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Move DebugSession to its own module.
This commit is contained in:
parent
7be33ed9d2
commit
72be9f33e0
3 changed files with 214 additions and 209 deletions
211
tests/helpers/debugsession.py
Normal file
211
tests/helpers/debugsession.py
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import warnings
|
||||
|
||||
from . import Closeable
|
||||
from .message import (
|
||||
raw_read_all as read_messages,
|
||||
raw_write_one as write_message
|
||||
)
|
||||
from .socket import create_client, close, recv_as_read, send_as_write
|
||||
from .threading import get_locked_and_waiter
|
||||
from .vsc import parse_message
|
||||
|
||||
|
||||
class DebugSessionConnection(Closeable):
|
||||
|
||||
VERBOSE = False
|
||||
#VERBOSE = True
|
||||
|
||||
TIMEOUT = 1.0
|
||||
|
||||
@classmethod
|
||||
def create(cls, addr, timeout=TIMEOUT):
|
||||
if timeout is None:
|
||||
timeout = cls.TIMEOUT
|
||||
sock = create_client()
|
||||
for _ in range(int(timeout * 10)):
|
||||
try:
|
||||
sock.connect(addr)
|
||||
except OSError:
|
||||
if cls.VERBOSE:
|
||||
print('+', end='')
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('could not connect')
|
||||
if cls.VERBOSE:
|
||||
print('connected')
|
||||
self = cls(sock, ownsock=True)
|
||||
self._addr = addr
|
||||
return self
|
||||
|
||||
def __init__(self, sock, ownsock=False):
|
||||
super(DebugSessionConnection, self).__init__()
|
||||
self._sock = sock
|
||||
self._ownsock = ownsock
|
||||
|
||||
def iter_messages(self):
|
||||
def stop():
|
||||
return self.closed
|
||||
read = recv_as_read(self._sock)
|
||||
for msg, _, _ in read_messages(read, stop=stop):
|
||||
if self.VERBOSE:
|
||||
print(msg)
|
||||
yield parse_message(msg)
|
||||
|
||||
def send(self, req):
|
||||
def stop():
|
||||
return self.closed
|
||||
write = send_as_write(self._sock)
|
||||
body = json.dumps(req)
|
||||
write_message(write, body, stop=stop)
|
||||
|
||||
# internal methods
|
||||
|
||||
def _close(self):
|
||||
if self._ownsock:
|
||||
close(self._sock)
|
||||
|
||||
|
||||
class DebugSession(Closeable):
|
||||
|
||||
VERBOSE = False
|
||||
#VERBOSE = True
|
||||
|
||||
@classmethod
|
||||
def create(cls, addr=('localhost', 8888), **kwargs):
|
||||
conn = DebugSessionConnection.create(addr)
|
||||
return cls(conn, owned=True, **kwargs)
|
||||
|
||||
def __init__(self, conn, seq=1000, handlers=(), timeout=None, owned=False):
|
||||
super(DebugSession, self).__init__()
|
||||
self._conn = conn
|
||||
self._seq = seq
|
||||
self._timeout = timeout
|
||||
self._owned = owned
|
||||
|
||||
self._handlers = []
|
||||
for handler in handlers:
|
||||
if callable(handler):
|
||||
self._add_handler(handler)
|
||||
else:
|
||||
self._add_handler(*handler)
|
||||
self._received = []
|
||||
self._listenerthread = threading.Thread(target=self._listen)
|
||||
self._listenerthread.start()
|
||||
|
||||
@property
|
||||
def received(self):
|
||||
return list(self._received)
|
||||
|
||||
def send_request(self, command, **args):
|
||||
wait = args.pop('wait', True)
|
||||
seq = self._seq
|
||||
self._seq += 1
|
||||
req = {
|
||||
'type': 'request',
|
||||
'seq': seq,
|
||||
'command': command,
|
||||
'arguments': args,
|
||||
}
|
||||
if wait:
|
||||
with self.wait_for_response(req):
|
||||
self._conn.send(req)
|
||||
else:
|
||||
self._conn.send(req)
|
||||
return req
|
||||
|
||||
def add_handler(self, handler, **kwargs):
|
||||
self._add_handler(handler, **kwargs)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wait_for_event(self, event, **kwargs):
|
||||
def match(msg):
|
||||
return msg.type == 'event' and msg.event == event
|
||||
handlername = 'event {!r}'.format(event)
|
||||
with self._wait_for_message(match, handlername, **kwargs):
|
||||
yield
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wait_for_response(self, req, **kwargs):
|
||||
try:
|
||||
command, seq = req.command, req.seq
|
||||
except AttributeError:
|
||||
command, seq = req['command'], req['seq']
|
||||
|
||||
def match(msg):
|
||||
if msg.type != 'response':
|
||||
return False
|
||||
return msg.request_seq == seq
|
||||
handlername = 'response (cmd:{} seq:{})'.format(command, seq)
|
||||
with self._wait_for_message(match, handlername, **kwargs):
|
||||
yield
|
||||
|
||||
# internal methods
|
||||
|
||||
def _close(self):
|
||||
if self._owned:
|
||||
self._conn.close()
|
||||
if self._listenerthread != threading.current_thread():
|
||||
self._listenerthread.join(timeout=1.0)
|
||||
if self._listenerthread.is_alive():
|
||||
warnings.warn('session listener still running')
|
||||
self._check_handlers()
|
||||
|
||||
def _listen(self):
|
||||
try:
|
||||
for msg in self._conn.iter_messages():
|
||||
if self.VERBOSE:
|
||||
print(' ->', msg)
|
||||
self._receive_message(msg)
|
||||
except EOFError:
|
||||
self.close()
|
||||
|
||||
def _receive_message(self, msg):
|
||||
for i, handler in enumerate(list(self._handlers)):
|
||||
handle_message, _, _ = handler
|
||||
handled = handle_message(msg)
|
||||
try:
|
||||
msg, handled = handled
|
||||
except TypeError:
|
||||
pass
|
||||
if handled:
|
||||
self._handlers.remove(handler)
|
||||
break
|
||||
self._received.append(msg)
|
||||
|
||||
def _add_handler(self, handle_msg, handlername=None, required=True):
|
||||
self._handlers.append(
|
||||
(handle_msg, handlername, required))
|
||||
|
||||
def _check_handlers(self):
|
||||
unhandled = []
|
||||
for handle_msg, name, required in self._handlers:
|
||||
if not required:
|
||||
continue
|
||||
unhandled.append(name or repr(handle_msg))
|
||||
if unhandled:
|
||||
raise RuntimeError('unhandled: {}'.format(unhandled))
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _wait_for_message(self, match, handlername, timeout=None):
|
||||
lock, wait = get_locked_and_waiter()
|
||||
|
||||
def handler(msg):
|
||||
if not match(msg):
|
||||
return msg, False
|
||||
lock.release()
|
||||
return msg, True
|
||||
self._add_handler(handler, handlername)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
wait(timeout or self._timeout, handlername)
|
||||
|
|
@ -1,215 +1,8 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from . import Closeable
|
||||
from .message import (
|
||||
raw_read_all as read_messages,
|
||||
raw_write_one as write_message
|
||||
)
|
||||
from .debugsession import DebugSession
|
||||
from .proc import Proc
|
||||
from .socket import create_client, close, recv_as_read, send_as_write
|
||||
from .threading import get_locked_and_waiter
|
||||
from .vsc import parse_message
|
||||
|
||||
|
||||
class DebugSessionConnection(Closeable):
|
||||
|
||||
VERBOSE = False
|
||||
#VERBOSE = True
|
||||
|
||||
TIMEOUT = 1.0
|
||||
|
||||
@classmethod
|
||||
def create(cls, addr, timeout=TIMEOUT):
|
||||
if timeout is None:
|
||||
timeout = cls.TIMEOUT
|
||||
sock = create_client()
|
||||
for _ in range(int(timeout * 10)):
|
||||
try:
|
||||
sock.connect(addr)
|
||||
except OSError:
|
||||
if cls.VERBOSE:
|
||||
print('+', end='')
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('could not connect')
|
||||
if cls.VERBOSE:
|
||||
print('connected')
|
||||
self = cls(sock, ownsock=True)
|
||||
self._addr = addr
|
||||
return self
|
||||
|
||||
def __init__(self, sock, ownsock=False):
|
||||
super(DebugSessionConnection, self).__init__()
|
||||
self._sock = sock
|
||||
self._ownsock = ownsock
|
||||
|
||||
def iter_messages(self):
|
||||
def stop():
|
||||
return self.closed
|
||||
read = recv_as_read(self._sock)
|
||||
for msg, _, _ in read_messages(read, stop=stop):
|
||||
if self.VERBOSE:
|
||||
print(msg)
|
||||
yield parse_message(msg)
|
||||
|
||||
def send(self, req):
|
||||
def stop():
|
||||
return self.closed
|
||||
write = send_as_write(self._sock)
|
||||
body = json.dumps(req)
|
||||
write_message(write, body, stop=stop)
|
||||
|
||||
# internal methods
|
||||
|
||||
def _close(self):
|
||||
if self._ownsock:
|
||||
close(self._sock)
|
||||
|
||||
|
||||
class DebugSession(Closeable):
|
||||
|
||||
VERBOSE = False
|
||||
#VERBOSE = True
|
||||
|
||||
@classmethod
|
||||
def create(cls, addr=('localhost', 8888), **kwargs):
|
||||
conn = DebugSessionConnection.create(addr)
|
||||
return cls(conn, owned=True, **kwargs)
|
||||
|
||||
def __init__(self, conn, seq=1000, handlers=(), timeout=None, owned=False):
|
||||
super(DebugSession, self).__init__()
|
||||
self._conn = conn
|
||||
self._seq = seq
|
||||
self._timeout = timeout
|
||||
self._owned = owned
|
||||
|
||||
self._handlers = []
|
||||
for handler in handlers:
|
||||
if callable(handler):
|
||||
self._add_handler(handler)
|
||||
else:
|
||||
self._add_handler(*handler)
|
||||
self._received = []
|
||||
self._listenerthread = threading.Thread(target=self._listen)
|
||||
self._listenerthread.start()
|
||||
|
||||
@property
|
||||
def received(self):
|
||||
return list(self._received)
|
||||
|
||||
def send_request(self, command, **args):
|
||||
wait = args.pop('wait', True)
|
||||
seq = self._seq
|
||||
self._seq += 1
|
||||
req = {
|
||||
'type': 'request',
|
||||
'seq': seq,
|
||||
'command': command,
|
||||
'arguments': args,
|
||||
}
|
||||
if wait:
|
||||
with self.wait_for_response(req):
|
||||
self._conn.send(req)
|
||||
else:
|
||||
self._conn.send(req)
|
||||
return req
|
||||
|
||||
def add_handler(self, handler, **kwargs):
|
||||
self._add_handler(handler, **kwargs)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wait_for_event(self, event, **kwargs):
|
||||
def match(msg):
|
||||
return msg.type == 'event' and msg.event == event
|
||||
handlername = 'event {!r}'.format(event)
|
||||
with self._wait_for_message(match, handlername, **kwargs):
|
||||
yield
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wait_for_response(self, req, **kwargs):
|
||||
try:
|
||||
command, seq = req.command, req.seq
|
||||
except AttributeError:
|
||||
command, seq = req['command'], req['seq']
|
||||
|
||||
def match(msg):
|
||||
if msg.type != 'response':
|
||||
return False
|
||||
return msg.request_seq == seq
|
||||
handlername = 'response (cmd:{} seq:{})'.format(command, seq)
|
||||
with self._wait_for_message(match, handlername, **kwargs):
|
||||
yield
|
||||
|
||||
# internal methods
|
||||
|
||||
def _close(self):
|
||||
if self._owned:
|
||||
self._conn.close()
|
||||
if self._listenerthread != threading.current_thread():
|
||||
self._listenerthread.join(timeout=1.0)
|
||||
if self._listenerthread.is_alive():
|
||||
warnings.warn('session listener still running')
|
||||
self._check_handlers()
|
||||
|
||||
def _listen(self):
|
||||
try:
|
||||
for msg in self._conn.iter_messages():
|
||||
if self.VERBOSE:
|
||||
print(' ->', msg)
|
||||
self._receive_message(msg)
|
||||
except EOFError:
|
||||
self.close()
|
||||
|
||||
def _receive_message(self, msg):
|
||||
for i, handler in enumerate(list(self._handlers)):
|
||||
handle_message, _, _ = handler
|
||||
handled = handle_message(msg)
|
||||
try:
|
||||
msg, handled = handled
|
||||
except TypeError:
|
||||
pass
|
||||
if handled:
|
||||
self._handlers.remove(handler)
|
||||
break
|
||||
self._received.append(msg)
|
||||
|
||||
def _add_handler(self, handle_msg, handlername=None, required=True):
|
||||
self._handlers.append(
|
||||
(handle_msg, handlername, required))
|
||||
|
||||
def _check_handlers(self):
|
||||
unhandled = []
|
||||
for handle_msg, name, required in self._handlers:
|
||||
if not required:
|
||||
continue
|
||||
unhandled.append(name or repr(handle_msg))
|
||||
if unhandled:
|
||||
raise RuntimeError('unhandled: {}'.format(unhandled))
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _wait_for_message(self, match, handlername, timeout=None):
|
||||
lock, wait = get_locked_and_waiter()
|
||||
|
||||
def handler(msg):
|
||||
if not match(msg):
|
||||
return msg, False
|
||||
lock.release()
|
||||
return msg, True
|
||||
self._add_handler(handler, handlername)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
wait(timeout or self._timeout, handlername)
|
||||
|
||||
|
||||
class DebugAdapter(Closeable):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import unittest
|
||||
|
||||
from tests.helpers.editor import FakeEditor, get_locked_and_waiter
|
||||
from tests.helpers.editor import FakeEditor
|
||||
from tests.helpers.threading import get_locked_and_waiter
|
||||
from tests.helpers.vsc import parse_message
|
||||
from tests.helpers.workspace import Workspace, PathEntry
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue