mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Fix linting errors.
This commit is contained in:
parent
4cdd5f5622
commit
4095e94e6d
10 changed files with 369 additions and 143 deletions
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# XXX Convert side-effects into explicit calls.
|
||||
import ptvsd.wrapper
|
||||
import pydevd
|
||||
pydevd.main()
|
||||
|
|
|
|||
|
|
@ -1,14 +1,24 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
DONT_DEBUG = []
|
||||
|
||||
|
||||
def debug(filename, port_num, debug_id, debug_options, run_as):
|
||||
import sys
|
||||
import ptvsd.wrapper
|
||||
# XXX docstring
|
||||
# XXX Convert side-effects into explicit calls.
|
||||
import ptvsd.wrapper
|
||||
import pydevd
|
||||
sys.argv[1:0] = ['--port', str(port_num), '--client', '127.0.0.1', '--file', filename]
|
||||
sys.argv[1:0] = [
|
||||
'--port', str(port_num),
|
||||
'--client', '127.0.0.1',
|
||||
'--file', filename,
|
||||
]
|
||||
pydevd.main()
|
||||
|
|
|
|||
|
|
@ -1,31 +1,40 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import print_function, with_statement, absolute_import
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
from ptvsd.reraise import reraise
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
import sys
|
||||
import threading
|
||||
from ptvsd.reraise import reraise
|
||||
|
||||
|
||||
class Future(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self, loop):
|
||||
self._lock = threading.Lock()
|
||||
self._loop = loop
|
||||
self._done = False
|
||||
self._observed = False
|
||||
self._done_callbacks = []
|
||||
self._exc_info = None
|
||||
|
||||
def __del__(self):
|
||||
with self._lock:
|
||||
if self._done and self._exc_info and not self._observed:
|
||||
print('Unobserved exception in a Future:', file=sys.__stderr__)
|
||||
traceback.print_exception(self._exc_info[0], self._exc_info[1], self._exc_info[2], file=sys.__stderr__)
|
||||
exctype, exc, tb = self._exc_info
|
||||
traceback.print_exception(exctype, exc, tb,
|
||||
file=sys.__stderr__)
|
||||
|
||||
def result(self):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
self._observed = True
|
||||
if self._exc_info:
|
||||
|
|
@ -33,32 +42,40 @@ class Future(object):
|
|||
return self._result
|
||||
|
||||
def exc_info(self):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
self._observed = True
|
||||
return self._exc_info
|
||||
|
||||
def set_result(self, result):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
self._result = result
|
||||
self._exc_info = None
|
||||
self._done = True
|
||||
callbacks = list(self._done_callbacks)
|
||||
|
||||
def invoke_callbacks():
|
||||
for cb in callbacks:
|
||||
cb(self)
|
||||
|
||||
self._loop.call_soon(invoke_callbacks)
|
||||
|
||||
def set_exc_info(self, exc_info):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
self._exc_info = exc_info
|
||||
self._done = True
|
||||
callbacks = list(self._done_callbacks)
|
||||
|
||||
def invoke_callbacks():
|
||||
for cb in callbacks:
|
||||
cb(self)
|
||||
|
||||
self._loop.call_soon(invoke_callbacks)
|
||||
|
||||
def add_done_callback(self, callback):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
done = self._done
|
||||
self._done_callbacks.append(callback)
|
||||
|
|
@ -66,11 +83,14 @@ class Future(object):
|
|||
callback(self)
|
||||
|
||||
def remove_done_callback(self, callback):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
self._done_callbacks.remove(callback)
|
||||
|
||||
|
||||
|
||||
class EventLoop(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self):
|
||||
self._queue = []
|
||||
self._lock = threading.Lock()
|
||||
|
|
@ -89,7 +109,7 @@ class EventLoop(object):
|
|||
self._event.clear()
|
||||
for (f, args) in queue:
|
||||
f(*args)
|
||||
|
||||
|
||||
def call_soon(self, f, *args):
|
||||
with self._lock:
|
||||
self._queue.append((f, args))
|
||||
|
|
@ -100,18 +120,24 @@ class EventLoop(object):
|
|||
|
||||
|
||||
class Result(object):
|
||||
# XXX docstring
|
||||
|
||||
__slots__ = ['value']
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
|
||||
|
||||
def async(f):
|
||||
# XXX docstring
|
||||
|
||||
def g(self, loop, *args, **kwargs):
|
||||
it = f(self, *args, **kwargs)
|
||||
result = Future(loop)
|
||||
if it is None:
|
||||
result.set_result(None)
|
||||
return result
|
||||
|
||||
def callback(fut):
|
||||
try:
|
||||
if fut is None:
|
||||
|
|
@ -131,6 +157,7 @@ def async(f):
|
|||
result.set_result(x.value)
|
||||
else:
|
||||
x.add_done_callback(callback)
|
||||
|
||||
callback(None)
|
||||
return result
|
||||
return g
|
||||
|
|
|
|||
110
ptvsd/ipcjson.py
110
ptvsd/ipcjson.py
|
|
@ -1,15 +1,15 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import print_function, with_statement, absolute_import
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
# This module MUST NOT import threading in global scope. This is because in a direct (non-ptvsd)
|
||||
# attach scenario, it is loaded on the injected debugger attach thread, and if threading module
|
||||
# hasn't been loaded already, it will assume that the thread on which it is being loaded is the
|
||||
# main thread. This will cause issues when the thread goes away after attach completes.
|
||||
# This module MUST NOT import threading in global scope. This is because
|
||||
# in a direct (non-ptvsd) attach scenario, it is loaded on the injected
|
||||
# debugger attach thread, and if threading module hasn't been loaded
|
||||
# already, it will assume that the thread on which it is being loaded is
|
||||
# the main thread. This will cause issues when the thread goes away
|
||||
# after attach completes.
|
||||
|
||||
import json
|
||||
import os.path
|
||||
|
|
@ -18,16 +18,32 @@ import socket
|
|||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
_TRACE = None
|
||||
|
||||
SKIP_TB_PREFIXES = [
|
||||
os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
from encodings import ascii
|
||||
|
||||
def to_bytes(cmd_str):
|
||||
return ascii.Codec.encode(cmd_str)[0]
|
||||
else:
|
||||
def to_bytes(cmd_str):
|
||||
return cmd_str
|
||||
|
||||
|
||||
def _trace(*msg):
|
||||
if _TRACE:
|
||||
_TRACE(''.join(_str_or_call(m) for m in msg) + '\n')
|
||||
|
||||
|
||||
def _str_or_call(m):
|
||||
try:
|
||||
callable = m.__call__
|
||||
|
|
@ -36,20 +52,20 @@ def _str_or_call(m):
|
|||
else:
|
||||
return str(callable())
|
||||
|
||||
def _trace(*msg):
|
||||
if _TRACE:
|
||||
_TRACE(''.join(_str_or_call(m) for m in msg) + '\n')
|
||||
|
||||
class InvalidHeaderError(Exception):
|
||||
# XXX docstring
|
||||
pass
|
||||
|
||||
|
||||
SKIP_TB_PREFIXES = [
|
||||
os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
|
||||
]
|
||||
class InvalidContentError(Exception):
|
||||
# XXX docstring
|
||||
pass
|
||||
|
||||
class InvalidHeaderError(Exception): pass
|
||||
|
||||
class InvalidContentError(Exception): pass
|
||||
|
||||
class SocketIO(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SocketIO, self).__init__(*args, **kwargs)
|
||||
self.__buffer = to_bytes('')
|
||||
|
|
@ -58,13 +74,17 @@ class SocketIO(object):
|
|||
self.__own_socket = kwargs.get('own_socket', True)
|
||||
self.__logfile = kwargs.get('logfile')
|
||||
if self.__socket is None and self.__port is None:
|
||||
raise ValueError("A 'port' or a 'socket' must be passed to SocketIO initializer as a keyword argument.")
|
||||
raise ValueError("A 'port' or a 'socket' must be passed to "
|
||||
"SocketIO initializer as a keyword argument.")
|
||||
if self.__socket is None:
|
||||
self.__socket = socket.create_connection(('127.0.0.1', self.__port))
|
||||
self.__socket = socket.create_connection(
|
||||
('127.0.0.1', self.__port))
|
||||
|
||||
def _send(self, **payload):
|
||||
# XXX docstring
|
||||
content = json.dumps(payload).encode('utf-8')
|
||||
headers = ('Content-Length: %d\r\n\r\n' % (len(content), )).encode('ascii')
|
||||
headers = ('Content-Length: {}\r\n\r\n'.format(len(content))
|
||||
).encode('ascii')
|
||||
if self.__logfile is not None:
|
||||
self.__logfile.write(content)
|
||||
self.__logfile.write('\n'.encode('utf-8'))
|
||||
|
|
@ -73,11 +93,12 @@ class SocketIO(object):
|
|||
self.__socket.send(content)
|
||||
|
||||
def _buffered_read_line_as_ascii(self):
|
||||
'''
|
||||
"""Return the next line from the buffer as a string.
|
||||
|
||||
Reads bytes until it encounters newline chars, and returns the bytes
|
||||
ascii decoded, newline chars are excluded from the return value.
|
||||
Blocks until: newline chars are read OR socket is closed.
|
||||
'''
|
||||
"""
|
||||
newline = '\r\n'.encode('ascii')
|
||||
while newline not in self.__buffer:
|
||||
temp = self.__socket.recv(1024)
|
||||
|
|
@ -98,6 +119,7 @@ class SocketIO(object):
|
|||
return line.decode('ascii', 'replace')
|
||||
|
||||
def _buffered_read_as_utf8(self, length):
|
||||
# XXX docstring
|
||||
while len(self.__buffer) < length:
|
||||
temp = self.__socket.recv(1024)
|
||||
if not temp:
|
||||
|
|
@ -105,14 +127,20 @@ class SocketIO(object):
|
|||
self.__buffer += temp
|
||||
|
||||
if len(self.__buffer) < length:
|
||||
raise InvalidContentError('Expected to read {0} bytes of content, but only read {1} bytes.'.format(length, len(self.__buffer)))
|
||||
raise InvalidContentError(('Expected to read {} bytes of content, '
|
||||
'but only read {} bytes.'
|
||||
).format(length, len(self.__buffer)))
|
||||
|
||||
content = self.__buffer[:length]
|
||||
self.__buffer = self.__buffer[length:]
|
||||
return content.decode('utf-8', 'replace')
|
||||
|
||||
def _wait_for_message(self):
|
||||
# base protocol defined at https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#base-protocol
|
||||
# XXX docstring
|
||||
# base protocol defined at:
|
||||
# https://github.com/Microsoft/language-server-protocol/
|
||||
# blob/master/protocol.md#base-protocol
|
||||
|
||||
# read all headers, ascii encoded separated by '\r\n'
|
||||
# end of headers is indicated by an empty line
|
||||
headers = {}
|
||||
|
|
@ -122,7 +150,8 @@ class SocketIO(object):
|
|||
if len(parts) == 2:
|
||||
headers[parts[0]] = parts[1]
|
||||
else:
|
||||
raise InvalidHeaderError("Malformed header, expected 'name: value'\n{0}".format(line))
|
||||
raise InvalidHeaderError(
|
||||
"Malformed header, expected 'name: value'\n" + line)
|
||||
line = self._buffered_read_line_as_ascii()
|
||||
|
||||
# end of stream
|
||||
|
|
@ -135,14 +164,16 @@ class SocketIO(object):
|
|||
try:
|
||||
length = int(length_text)
|
||||
except ValueError:
|
||||
raise InvalidHeaderError("Invalid Content-Length: {0}".format(length_text))
|
||||
raise InvalidHeaderError(
|
||||
'Invalid Content-Length: ' + length_text)
|
||||
except NameError:
|
||||
raise InvalidHeaderError('Content-Length not specified in headers')
|
||||
except KeyError:
|
||||
raise InvalidHeaderError('Content-Length not specified in headers')
|
||||
|
||||
if length < 0 or length > 2147483647:
|
||||
raise InvalidHeaderError("Invalid Content-Length: {0}".format(length))
|
||||
raise InvalidHeaderError(
|
||||
'Invalid Content-Length: ' + length_text)
|
||||
|
||||
# read content, utf-8 encoded
|
||||
content = self._buffered_read_as_utf8(length)
|
||||
|
|
@ -155,11 +186,15 @@ class SocketIO(object):
|
|||
raise InvalidContentError('Error deserializing message content.')
|
||||
|
||||
def _close(self):
|
||||
# XXX docstring
|
||||
if self.__own_socket:
|
||||
self.__socket.close()
|
||||
|
||||
|
||||
'''
|
||||
class StandardIO(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self, stdin, stdout, *args, **kwargs):
|
||||
super(StandardIO, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
|
|
@ -175,20 +210,24 @@ class StandardIO(object):
|
|||
self.__stdout.flush()
|
||||
|
||||
def _wait_for_message(self):
|
||||
msg = json.loads(self.__stdin.readline().decode('utf-8', 'replace').rstrip())
|
||||
msg = json.loads(
|
||||
self.__stdin.readline().decode('utf-8', 'replace').rstrip())
|
||||
self._receive_message(msg)
|
||||
|
||||
def _close(self):
|
||||
pass
|
||||
'''
|
||||
|
||||
|
||||
class IpcChannel(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# This class is meant to be last in the list of base classes
|
||||
# Don't call super because object's __init__ doesn't take arguments
|
||||
try:
|
||||
import thread
|
||||
except:
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
self.__seq = itertools.count()
|
||||
self.__exit = False
|
||||
|
|
@ -197,9 +236,11 @@ class IpcChannel(object):
|
|||
self.__exit_on_unknown_command = True
|
||||
|
||||
def close(self):
|
||||
# XXX docstring
|
||||
self._close()
|
||||
|
||||
def send_event(self, _name, **kwargs):
|
||||
# XXX docstring
|
||||
with self.__lock:
|
||||
self._send(
|
||||
type='event',
|
||||
|
|
@ -209,6 +250,7 @@ class IpcChannel(object):
|
|||
)
|
||||
|
||||
def send_response(self, request, success=True, message=None, **kwargs):
|
||||
# XXX docstring
|
||||
with self.__lock:
|
||||
self._send(
|
||||
type='response',
|
||||
|
|
@ -221,14 +263,17 @@ class IpcChannel(object):
|
|||
)
|
||||
|
||||
def set_exit(self):
|
||||
# XXX docstring
|
||||
self.__exit = True
|
||||
|
||||
def process_messages(self):
|
||||
# XXX docstring
|
||||
while True:
|
||||
if self.process_one_message():
|
||||
return
|
||||
|
||||
def process_one_message(self):
|
||||
# XXX docstring
|
||||
try:
|
||||
msg = self.__message.pop(0)
|
||||
except IndexError:
|
||||
|
|
@ -259,7 +304,9 @@ class IpcChannel(object):
|
|||
return self.__exit
|
||||
|
||||
def on_request(self, request):
|
||||
assert request.get('type', '') == 'request', "Only handle 'request' messages in on_request"
|
||||
# XXX docstring
|
||||
assert request.get('type', '') == 'request', \
|
||||
"Only handle 'request' messages in on_request"
|
||||
|
||||
cmd = request.get('command', '')
|
||||
args = request.get('arguments', {})
|
||||
|
|
@ -277,14 +324,17 @@ class IpcChannel(object):
|
|||
)
|
||||
|
||||
def on_response(self, msg):
|
||||
# XXX docstring
|
||||
# this class is only used for server side only for now
|
||||
raise NotImplementedError
|
||||
|
||||
def on_event(self, msg):
|
||||
# XXX docstring
|
||||
# this class is only used for server side only for now
|
||||
raise NotImplementedError
|
||||
|
||||
def on_invalid_request(self, request, args):
|
||||
# XXX docstring
|
||||
self.send_response(request, success=False, message='Unknown command')
|
||||
if self.__exit_on_unknown_command:
|
||||
self.__exit = True
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
# XXX only absolute_import needed?
|
||||
from __future__ import print_function, with_statement, absolute_import
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
from ptvsd.reraise3 import reraise
|
||||
from ptvsd.reraise3 import reraise # noqa: F401
|
||||
else:
|
||||
from ptvsd.reraise2 import reraise
|
||||
from ptvsd.reraise2 import reraise # noqa: F401
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
# XXX not needed?
|
||||
from __future__ import print_function, with_statement, absolute_import
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
|
||||
def reraise(exc_info):
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
# XXX docstring
|
||||
raise exc_info[0], exc_info[1], exc_info[2] # noqa
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
# 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
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
|
||||
def reraise(exc_info):
|
||||
# XXX docstring
|
||||
raise exc_info[1].with_traceback(exc_info[2])
|
||||
|
|
|
|||
293
ptvsd/wrapper.py
293
ptvsd/wrapper.py
|
|
@ -1,66 +1,79 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
# XXX with_statement is not needed
|
||||
from __future__ import print_function, with_statement, absolute_import
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
import io
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import traceback
|
||||
import untangle
|
||||
import untangle
|
||||
|
||||
try:
|
||||
import urllib
|
||||
urllib.unquote
|
||||
except:
|
||||
except Exception:
|
||||
import urllib.parse as urllib
|
||||
|
||||
import _pydevd_bundle.pydevd_comm as pydevd_comm
|
||||
from _pydevd_bundle.pydevd_comm import pydevd_log
|
||||
#from _pydevd_bundle.pydevd_comm import pydevd_log
|
||||
|
||||
import ptvsd.ipcjson as ipcjson
|
||||
import ptvsd.futures as futures
|
||||
|
||||
|
||||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
|
||||
__version__ = "4.0.0a1"
|
||||
|
||||
|
||||
#def ipcjson_trace(s):
|
||||
# print(s)
|
||||
#ipcjson._TRACE = ipcjson_trace
|
||||
|
||||
|
||||
def unquote(s):
|
||||
if s is None:
|
||||
return None
|
||||
return urllib.unquote(s)
|
||||
|
||||
# Generates VSCode entity IDs, and maps them to corresponding pydevd entity IDs.
|
||||
#
|
||||
# For VSCode, IDs are always integers, and uniquely identify the entity among all other
|
||||
# entities of the same type - e.g. all frames across all threads have unique IDs.
|
||||
#
|
||||
# For pydevd, IDs can be integer or strings, and are usually specific to some scope -
|
||||
# for example, a frame ID is only unique within a given thread. To produce a truly unique
|
||||
# ID, the IDs of all the outer scopes have to be combined into a tuple. Thus, for example,
|
||||
# a pydevd frame ID is (thread_id, frame_id).
|
||||
#
|
||||
# Variables (evaluation results) technically don't have IDs in pydevd, as it doesn't have
|
||||
# evaluation persistence. However, for a given frame, any child can be identified by the
|
||||
# path one needs to walk from the root of the frame to get to that child - and that path,
|
||||
# represented as a sequence of its consituent components, is used by pydevd commands to
|
||||
# identify the variable. So we use the tuple representation of the same as its pydevd ID.
|
||||
# For example, for something like foo[1].bar, its ID is:
|
||||
# (thread_id, frame_id, 'FRAME', 'foo', 1, 'bar')
|
||||
#
|
||||
# For pydevd breakpoints, the ID has to be specified by the caller when creating, so we
|
||||
# can just reuse the ID that was generated for VSC. However, when referencing the pydevd
|
||||
# breakpoint later (e.g. to remove it), its ID must be specified together with path to
|
||||
# file in which that breakpoint is set - i.e. pydevd treats those IDs as scoped to a file.
|
||||
# So, even though breakpoint IDs are unique across files, use (path, bp_id) as pydevd ID.
|
||||
|
||||
class IDMap(object):
|
||||
"""Maps VSCode entities to corresponding pydevd entities by ID.
|
||||
|
||||
VSCode entity IDs are generated here when necessary.
|
||||
|
||||
For VSCode, entity IDs are always integers, and uniquely identify
|
||||
the entity among all other entities of the same type - e.g. all
|
||||
frames across all threads have unique IDs.
|
||||
|
||||
For pydevd, IDs can be integer or strings, and are usually specific
|
||||
to some scope - for example, a frame ID is only unique within a
|
||||
given thread. To produce a truly unique ID, the IDs of all the outer
|
||||
scopes have to be combined into a tuple. Thus, for example, a pydevd
|
||||
frame ID is (thread_id, frame_id).
|
||||
|
||||
Variables (evaluation results) technically don't have IDs in pydevd,
|
||||
as it doesn't have evaluation persistence. However, for a given
|
||||
frame, any child can be identified by the path one needs to walk
|
||||
from the root of the frame to get to that child - and that path,
|
||||
represented as a sequence of its consituent components, is used by
|
||||
pydevd commands to identify the variable. So we use the tuple
|
||||
representation of the same as its pydevd ID. For example, for
|
||||
something like foo[1].bar, its ID is:
|
||||
(thread_id, frame_id, 'FRAME', 'foo', 1, 'bar')
|
||||
|
||||
For pydevd breakpoints, the ID has to be specified by the caller
|
||||
when creating, so we can just reuse the ID that was generated for
|
||||
VSC. However, when referencing the pydevd breakpoint later (e.g. to
|
||||
remove it), its ID must be specified together with path to file in
|
||||
which that breakpoint is set - i.e. pydevd treats those IDs as
|
||||
scoped to a file. So, even though breakpoint IDs are unique across
|
||||
files, use (path, bp_id) as pydevd ID.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._vscode_to_pydevd = {}
|
||||
self._pydevd_to_vscode = {}
|
||||
|
|
@ -68,10 +81,12 @@ class IDMap(object):
|
|||
self._lock = threading.Lock()
|
||||
|
||||
def pairs(self):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
return list(self._pydevd_to_vscode.items())
|
||||
|
||||
|
||||
def add(self, pydevd_id):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
vscode_id = self._next_id
|
||||
if callable(pydevd_id):
|
||||
|
|
@ -82,6 +97,7 @@ class IDMap(object):
|
|||
return vscode_id
|
||||
|
||||
def remove(self, pydevd_id=None, vscode_id=None):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
if pydevd_id is None:
|
||||
pydevd_id = self._vscode_to_pydevd[vscode_id]
|
||||
|
|
@ -89,11 +105,13 @@ class IDMap(object):
|
|||
vscode_id = self._pydevd_to_vscode[pydevd_id]
|
||||
del self._vscode_to_pydevd[vscode_id]
|
||||
del self._pydevd_to_vscode[pydevd_id]
|
||||
|
||||
|
||||
def to_pydevd(self, vscode_id):
|
||||
# XXX docstring
|
||||
return self._vscode_to_pydevd[vscode_id]
|
||||
|
||||
def to_vscode(self, pydevd_id, autogen=True):
|
||||
# XXX docstring
|
||||
try:
|
||||
return self._pydevd_to_vscode[pydevd_id]
|
||||
except KeyError:
|
||||
|
|
@ -103,25 +121,35 @@ class IDMap(object):
|
|||
raise
|
||||
|
||||
def pydevd_ids(self):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
ids = list(self._pydevd_to_vscode.keys())
|
||||
return ids
|
||||
|
||||
def vscode_ids(self):
|
||||
# XXX docstring
|
||||
with self._lock:
|
||||
ids = list(self._vscode_to_pydevd.keys())
|
||||
return ids
|
||||
|
||||
|
||||
class ExceptionInfo(object):
|
||||
# XXX docstring
|
||||
|
||||
def __init__(self, name, description):
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
# A dummy socket-like object that is given to pydevd in lieu of the real thing.
|
||||
# It parses pydevd messages and redirects them to the provided handler callback.
|
||||
# It also provides an interface to send notifications and requests to pydevd;
|
||||
# for requests, the reply can be asynchronously awaited.
|
||||
|
||||
class PydevdSocket(object):
|
||||
"""A dummy socket-like object for communicating with pydevd.
|
||||
|
||||
It parses pydevd messages and redirects them to the provided handler
|
||||
callback. It also provides an interface to send notifications and
|
||||
requests to pydevd; for requests, the reply can be asynchronously
|
||||
awaited.
|
||||
"""
|
||||
|
||||
def __init__(self, event_handler):
|
||||
#self.log = open('pydevd.log', 'w')
|
||||
self.event_handler = event_handler
|
||||
|
|
@ -131,18 +159,22 @@ class PydevdSocket(object):
|
|||
self.requests = {}
|
||||
|
||||
def close(self):
|
||||
# XXX docstring
|
||||
pass
|
||||
|
||||
def shutdown(self, mode):
|
||||
# XXX docstring
|
||||
pass
|
||||
|
||||
def recv(self, count):
|
||||
# XXX docstring
|
||||
data = os.read(self.pipe_r, count)
|
||||
#self.log.write('>>>[' + data.decode('utf8') + ']\n\n')
|
||||
#self.log.flush()
|
||||
return data
|
||||
|
||||
def send(self, data):
|
||||
# XXX docstring
|
||||
result = len(data)
|
||||
data = unquote(data.decode('utf8'))
|
||||
#self.log.write('<<<[' + data + ']\n\n')
|
||||
|
|
@ -159,17 +191,20 @@ class PydevdSocket(object):
|
|||
return result
|
||||
|
||||
def make_packet(self, cmd_id, args):
|
||||
# XXX docstring
|
||||
with self.lock:
|
||||
seq = self.seq
|
||||
self.seq += 1
|
||||
s = "%s\t%s\t%s\n" % (cmd_id, seq, args)
|
||||
s = '{}\t{}\t{}\n'.format(cmd_id, seq, args)
|
||||
return seq, s
|
||||
|
||||
def pydevd_notify(self, cmd_id, args):
|
||||
# XXX docstring
|
||||
seq, s = self.make_packet(cmd_id, args)
|
||||
os.write(self.pipe_w, s.encode('utf8'))
|
||||
|
||||
def pydevd_request(self, loop, cmd_id, args):
|
||||
# XXX docstring
|
||||
seq, s = self.make_packet(cmd_id, args)
|
||||
fut = loop.create_future()
|
||||
with self.lock:
|
||||
|
|
@ -177,10 +212,18 @@ class PydevdSocket(object):
|
|||
os.write(self.pipe_w, s.encode('utf8'))
|
||||
return fut
|
||||
|
||||
# IPC JSON message processor for VSC debugger protocol, mapping it to pydevd protocol.
|
||||
|
||||
class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
||||
"""IPC JSON message processor for VSC debugger protocol.
|
||||
|
||||
This translates between the VSC debugger protocol and the pydevd
|
||||
protocol.
|
||||
"""
|
||||
|
||||
def __init__(self, socket, pydevd, logfile=None):
|
||||
super(VSCodeMessageProcessor, self).__init__(socket=socket, own_socket=False, logfile=logfile)
|
||||
super(VSCodeMessageProcessor, self).__init__(socket=socket,
|
||||
own_socket=False,
|
||||
logfile=logfile)
|
||||
self.socket = socket
|
||||
self.pydevd = pydevd
|
||||
self.stack_traces = {}
|
||||
|
|
@ -193,25 +236,32 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
self.bp_map = IDMap()
|
||||
self.next_var_ref = 0
|
||||
self.loop = futures.EventLoop()
|
||||
threading.Thread(target = self.loop.run_forever, name = 'ptvsd.EventLoop').start()
|
||||
|
||||
t = threading.Thread(target=self.loop.run_forever,
|
||||
name='ptvsd.EventLoop')
|
||||
t.start()
|
||||
|
||||
def close(self):
|
||||
# XXX docstring
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
|
||||
def pydevd_notify(self, cmd_id, args):
|
||||
# XXX docstring
|
||||
try:
|
||||
return self.pydevd.pydevd_notify(cmd_id, args)
|
||||
except:
|
||||
except BaseException:
|
||||
traceback.print_exc(file=sys.__stderr__)
|
||||
raise
|
||||
|
||||
def pydevd_request(self, cmd_id, args):
|
||||
# XXX docstring
|
||||
return self.pydevd.pydevd_request(self.loop, cmd_id, args)
|
||||
|
||||
# Instances of this class provide decorators to mark methods as handlers for various
|
||||
# pydevd messages - a decorated method is added to the map with the corresponding
|
||||
# message ID, and is looked up there by pydevd event handler below.
|
||||
# Instances of this class provide decorators to mark methods as
|
||||
# handlers for various # pydevd messages - a decorated method is
|
||||
# added to the map with the corresponding message ID, and is
|
||||
# looked up there by pydevd event handler below.
|
||||
class EventHandlers(dict):
|
||||
def handler(self, cmd_id):
|
||||
def decorate(f):
|
||||
|
|
@ -222,6 +272,7 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
pydevd_events = EventHandlers()
|
||||
|
||||
def on_pydevd_event(self, cmd_id, seq, args):
|
||||
# XXX docstring
|
||||
try:
|
||||
f = self.pydevd_events[cmd_id]
|
||||
except KeyError:
|
||||
|
|
@ -229,50 +280,64 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
return f(self, seq, args)
|
||||
|
||||
def async_handler(m):
|
||||
# XXX docstring
|
||||
m = futures.async(m)
|
||||
|
||||
def f(self, *args, **kwargs):
|
||||
fut = m(self, self.loop, *args, **kwargs)
|
||||
|
||||
def done(fut):
|
||||
try:
|
||||
fut.result()
|
||||
except:
|
||||
except BaseException:
|
||||
traceback.print_exc(file=sys.__stderr__)
|
||||
|
||||
fut.add_done_callback(done)
|
||||
|
||||
return f
|
||||
|
||||
@async_handler
|
||||
def on_initialize(self, request, args):
|
||||
yield self.pydevd_request(pydevd_comm.CMD_VERSION, '1.1\tWINDOWS\tID')
|
||||
self.send_response(request,
|
||||
supportsExceptionInfoRequest=True,
|
||||
supportsConfigurationDoneRequest=True,
|
||||
exceptionBreakpointFilters=[
|
||||
{'filter': 'raised', 'label': 'Raised Exceptions'},
|
||||
{'filter': 'uncaught', 'label': 'Uncaught Exceptions'},
|
||||
]
|
||||
)
|
||||
# XXX docstring
|
||||
cmd = pydevd_comm.CMD_VERSION
|
||||
args = '1.1\tWINDOWS\tID'
|
||||
yield self.pydevd_request(cmd, args)
|
||||
self.send_response(
|
||||
request,
|
||||
supportsExceptionInfoRequest=True,
|
||||
supportsConfigurationDoneRequest=True,
|
||||
exceptionBreakpointFilters=[
|
||||
{'filter': 'raised', 'label': 'Raised Exceptions'},
|
||||
{'filter': 'uncaught', 'label': 'Uncaught Exceptions'},
|
||||
],
|
||||
)
|
||||
self.send_event('initialized')
|
||||
|
||||
@async_handler
|
||||
def on_configurationDone(self, request, args):
|
||||
# XXX docstring
|
||||
self.send_response(request)
|
||||
yield self.pydevd_request(pydevd_comm.CMD_RUN, '')
|
||||
self.send_process_event(self.start_reason)
|
||||
|
||||
def on_disconnect(self, request, args):
|
||||
# XXX docstring
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_attach(self, request, args):
|
||||
# XXX docstring
|
||||
self.start_reason = 'attach'
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_launch(self, request, args):
|
||||
# XXX docstring
|
||||
self.start_reason = 'launch'
|
||||
self.send_response(request)
|
||||
|
||||
def send_process_event(self, start_method):
|
||||
# XXX docstring
|
||||
evt = {
|
||||
'name': sys.argv[0],
|
||||
'systemProcessId': os.getpid(),
|
||||
|
|
@ -283,7 +348,9 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@async_handler
|
||||
def on_threads(self, request, args):
|
||||
_, _, args = yield self.pydevd_request(pydevd_comm.CMD_LIST_THREADS, '')
|
||||
# XXX docstring
|
||||
_, _, args = yield self.pydevd_request(
|
||||
pydevd_comm.CMD_LIST_THREADS, '')
|
||||
xml = untangle.parse(args).xml
|
||||
try:
|
||||
xthreads = xml.thread
|
||||
|
|
@ -296,14 +363,15 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
try:
|
||||
name = unquote(xthread['name'])
|
||||
except KeyError:
|
||||
name = None
|
||||
name = None
|
||||
if not (name and name.startswith('pydevd.')):
|
||||
threads.append({'id': tid, 'name': name})
|
||||
|
||||
|
||||
self.send_response(request, threads=threads)
|
||||
|
||||
@async_handler
|
||||
def on_stackTrace(self, request, args):
|
||||
# XXX docstring
|
||||
tid = int(args['threadId'])
|
||||
startFrame = int(args['startFrame'])
|
||||
levels = int(args['levels'])
|
||||
|
|
@ -328,21 +396,34 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
name = unquote(xframe['name'])
|
||||
file = unquote(xframe['file'])
|
||||
line = int(xframe['line'])
|
||||
stackFrames.append({'id': fid, 'name': name, 'source': {'path': file}, 'line': line, 'column': 0})
|
||||
stackFrames.append({
|
||||
'id': fid,
|
||||
'name': name,
|
||||
'source': {'path': file},
|
||||
'line': line, 'column': 0,
|
||||
})
|
||||
|
||||
self.send_response(request, stackFrames=stackFrames, totalFrames=totalFrames)
|
||||
self.send_response(request,
|
||||
stackFrames=stackFrames,
|
||||
totalFrames=totalFrames)
|
||||
|
||||
@async_handler
|
||||
def on_scopes(self, request, args):
|
||||
# XXX docstring
|
||||
vsc_fid = int(args['frameId'])
|
||||
pyd_tid, pyd_fid = self.frame_map.to_pydevd(vsc_fid)
|
||||
pyd_var = (pyd_tid, pyd_fid, 'FRAME')
|
||||
vsc_var = self.var_map.to_vscode(pyd_var)
|
||||
scope = {'name': 'Locals', 'expensive': False, 'variablesReference': vsc_var}
|
||||
scope = {
|
||||
'name': 'Locals',
|
||||
'expensive': False,
|
||||
'variablesReference': vsc_var,
|
||||
}
|
||||
self.send_response(request, scopes=[scope])
|
||||
|
||||
@async_handler
|
||||
def on_variables(self, request, args):
|
||||
# XXX docstring
|
||||
vsc_var = int(args['variablesReference'])
|
||||
pyd_var = self.var_map.to_pydevd(vsc_var)
|
||||
|
||||
|
|
@ -350,8 +431,8 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
cmd = pydevd_comm.CMD_GET_FRAME
|
||||
else:
|
||||
cmd = pydevd_comm.CMD_GET_VARIABLE
|
||||
|
||||
_, _, args = yield self.pydevd_request(cmd, '\t'.join(str(s) for s in pyd_var))
|
||||
cmdargs = '\t'.join(str(s) for s in pyd_var)
|
||||
_, _, args = yield self.pydevd_request(cmd, cmdargs)
|
||||
xml = untangle.parse(args).xml
|
||||
try:
|
||||
xvars = xml.var
|
||||
|
|
@ -374,8 +455,9 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@async_handler
|
||||
def on_pause(self, request, args):
|
||||
# XXX docstring
|
||||
vsc_tid = int(args['threadId'])
|
||||
if vsc_tid == 0: # VS does this to mean "stop all threads":
|
||||
if vsc_tid == 0: # VS does this to mean "stop all threads":
|
||||
for pyd_tid in self.thread_map.pydevd_ids():
|
||||
self.pydevd_notify(pydevd_comm.CMD_THREAD_SUSPEND, pyd_tid)
|
||||
else:
|
||||
|
|
@ -385,77 +467,100 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@async_handler
|
||||
def on_continue(self, request, args):
|
||||
# XXX docstring
|
||||
tid = self.thread_map.to_pydevd(int(args['threadId']))
|
||||
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, tid)
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_next(self, request, args):
|
||||
# XXX docstring
|
||||
tid = self.thread_map.to_pydevd(int(args['threadId']))
|
||||
self.pydevd_notify(pydevd_comm.CMD_STEP_OVER, tid)
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_stepIn(self, request, args):
|
||||
# XXX docstring
|
||||
tid = self.thread_map.to_pydevd(int(args['threadId']))
|
||||
self.pydevd_notify(pydevd_comm.CMD_STEP_INTO, tid)
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_stepOut(self, request, args):
|
||||
# XXX docstring
|
||||
tid = self.thread_map.to_pydevd(int(args['threadId']))
|
||||
self.pydevd_notify(pydevd_comm.CMD_STEP_RETURN, tid)
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_setBreakpoints(self, request, args):
|
||||
# XXX docstring
|
||||
bps = []
|
||||
path = args['source']['path']
|
||||
src_bps = args.get('breakpoints', [])
|
||||
|
||||
# First, we must delete all existing breakpoints in that source.
|
||||
for pyd_bpid, vsc_bpid in self.bp_map.pairs():
|
||||
self.pydevd_notify(pydevd_comm.CMD_REMOVE_BREAK, 'python-line\t%s\t%s' % (path, vsc_bpid))
|
||||
self.pydevd_notify(pydevd_comm.CMD_REMOVE_BREAK,
|
||||
'python-line\t{}\t{}'.format(path, vsc_bpid))
|
||||
self.bp_map.remove(pyd_bpid, vsc_bpid)
|
||||
|
||||
for src_bp in src_bps:
|
||||
line = src_bp['line']
|
||||
vsc_bpid = self.bp_map.add(lambda vsc_bpid: (path, vsc_bpid))
|
||||
self.pydevd_notify(pydevd_comm.CMD_SET_BREAK, '%s\tpython-line\t%s\t%s\tNone\tNone\tNone' %
|
||||
(vsc_bpid, path, line))
|
||||
bps.append({ 'id': vsc_bpid, 'verified': True, 'line': line })
|
||||
self.pydevd_notify(pydevd_comm.CMD_SET_BREAK,
|
||||
('{}\tpython-line\t{}\t{}\tNone\tNone\tNone'
|
||||
).format(vsc_bpid, path, line))
|
||||
bps.append({
|
||||
'id': vsc_bpid,
|
||||
'verified': True,
|
||||
'line': line,
|
||||
})
|
||||
|
||||
self.send_response(request, breakpoints=bps)
|
||||
|
||||
@async_handler
|
||||
def on_setExceptionBreakpoints(self, request, args):
|
||||
self.pydevd_notify(pydevd_comm.CMD_REMOVE_EXCEPTION_BREAK, 'python-BaseException')
|
||||
# XXX docstring
|
||||
self.pydevd_notify(pydevd_comm.CMD_REMOVE_EXCEPTION_BREAK,
|
||||
'python-BaseException')
|
||||
filters = args['filters']
|
||||
break_raised = 'raised' in filters
|
||||
break_uncaught = 'uncaught' in filters
|
||||
if break_raised or break_uncaught:
|
||||
self.pydevd_notify(pydevd_comm.CMD_ADD_EXCEPTION_BREAK, 'python-BaseException\t%s\t%s\t%s' % (
|
||||
2 if break_raised else 0, 1 if break_uncaught else 0, 0))
|
||||
self.pydevd_notify(pydevd_comm.CMD_ADD_EXCEPTION_BREAK,
|
||||
('python-BaseException\t{}\t{}\t{}'
|
||||
).format(2 if break_raised else 0,
|
||||
1 if break_uncaught else 0,
|
||||
0))
|
||||
self.send_response(request)
|
||||
|
||||
@async_handler
|
||||
def on_exceptionInfo(self, request, args):
|
||||
# XXX docstring
|
||||
tid = self.thread_map.to_pydevd(args['threadId'])
|
||||
with self.active_exceptions_lock:
|
||||
exc = self.active_exceptions[tid]
|
||||
self.send_response(request, exceptionId=exc.name, description=exc.description, breakMode='unhandled', details={
|
||||
'typeName': exc.name,
|
||||
'message': exc.description,
|
||||
})
|
||||
self.send_response(request,
|
||||
exceptionId=exc.name,
|
||||
description=exc.description,
|
||||
breakMode='unhandled',
|
||||
details={'typeName': exc.name,
|
||||
'message': exc.description,
|
||||
},
|
||||
)
|
||||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_THREAD_CREATE)
|
||||
def on_pydevd_thread_create(self, seq, args):
|
||||
# XXX docstring
|
||||
xml = untangle.parse(args).xml
|
||||
tid = self.thread_map.to_vscode(xml.thread['id'])
|
||||
self.send_event('thread', reason='started', threadId=tid)
|
||||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_THREAD_KILL)
|
||||
def on_pydevd_thread_kill(self, seq, args):
|
||||
# XXX docstring
|
||||
try:
|
||||
tid = self.thread_map.to_vscode(args, autogen=False)
|
||||
except KeyError:
|
||||
|
|
@ -465,10 +570,16 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_THREAD_SUSPEND)
|
||||
def on_pydevd_thread_suspend(self, seq, args):
|
||||
# XXX docstring
|
||||
xml = untangle.parse(args).xml
|
||||
tid = xml.thread['id']
|
||||
reason = int(xml.thread['stop_reason'])
|
||||
if reason in (pydevd_comm.CMD_STEP_INTO, pydevd_comm.CMD_STEP_OVER, pydevd_comm.CMD_STEP_RETURN):
|
||||
STEP_REASONS = {
|
||||
pydevd_comm.CMD_STEP_INTO,
|
||||
pydevd_comm.CMD_STEP_OVER,
|
||||
pydevd_comm.CMD_STEP_RETURN,
|
||||
}
|
||||
if reason in STEP_REASONS:
|
||||
reason = 'step'
|
||||
elif reason == pydevd_comm.CMD_STEP_CAUGHT_EXCEPTION:
|
||||
reason = 'exception'
|
||||
|
|
@ -476,17 +587,19 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
reason = 'breakpoint'
|
||||
else:
|
||||
reason = 'pause'
|
||||
with self.stack_traces_lock:
|
||||
with self.stack_traces_lock:
|
||||
self.stack_traces[tid] = xml.thread.frame
|
||||
tid = self.thread_map.to_vscode(tid)
|
||||
self.send_event('stopped', reason=reason, threadId=tid)
|
||||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_THREAD_RUN)
|
||||
def on_pydevd_thread_run(self, seq, args):
|
||||
# XXX docstring
|
||||
pyd_tid, reason = args.split('\t')
|
||||
vsc_tid = self.thread_map.to_vscode(pyd_tid)
|
||||
|
||||
# Stack trace, and all frames and variables for this thread are now invalid; clear their IDs.
|
||||
# Stack trace, and all frames and variables for this thread
|
||||
# are now invalid; clear their IDs.
|
||||
with self.stack_traces_lock:
|
||||
del self.stack_traces[pyd_tid]
|
||||
|
||||
|
|
@ -502,6 +615,7 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_SEND_CURR_EXCEPTION_TRACE)
|
||||
def on_pydevd_send_curr_exception_trace(self, seq, args):
|
||||
# XXX docstring
|
||||
_, name, description, xml = args.split('\t')
|
||||
xml = untangle.parse(xml).xml
|
||||
pyd_tid = xml.thread['id']
|
||||
|
|
@ -510,11 +624,15 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel):
|
|||
|
||||
@pydevd_events.handler(pydevd_comm.CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED)
|
||||
def on_pydevd_send_curr_exception_trace_proceeded(self, seq, args):
|
||||
# XXX docstring
|
||||
pass
|
||||
|
||||
|
||||
def start_server(port):
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||
# XXX docstring
|
||||
server = socket.socket(socket.AF_INET,
|
||||
socket.SOCK_STREAM,
|
||||
socket.IPPROTO_TCP)
|
||||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server.bind(('127.0.0.1', port))
|
||||
server.listen(1)
|
||||
|
|
@ -523,24 +641,31 @@ def start_server(port):
|
|||
pydevd = PydevdSocket(lambda *args: proc.on_pydevd_event(*args))
|
||||
proc = VSCodeMessageProcessor(client, pydevd)
|
||||
|
||||
server_thread = threading.Thread(target = proc.process_messages, name = 'ptvsd.Server')
|
||||
server_thread = threading.Thread(target=proc.process_messages,
|
||||
name='ptvsd.Server')
|
||||
server_thread.start()
|
||||
|
||||
return pydevd
|
||||
|
||||
|
||||
def start_client(host, port):
|
||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||
# XXX docstring
|
||||
client = socket.socket(socket.AF_INET,
|
||||
socket.SOCK_STREAM,
|
||||
socket.IPPROTO_TCP)
|
||||
client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
client.connect((host, port))
|
||||
|
||||
pydevd = PydevdSocket(lambda *args: proc.on_pydevd_event(*args))
|
||||
proc = VSCodeMessageProcessor(client, pydevd)
|
||||
|
||||
server_thread = threading.Thread(target = proc.process_messages, name = 'ptvsd.Client')
|
||||
server_thread = threading.Thread(target=proc.process_messages,
|
||||
name='ptvsd.Client')
|
||||
server_thread.start()
|
||||
|
||||
return pydevd
|
||||
|
||||
|
||||
|
||||
# These are the functions pydevd invokes to get a socket to the client.
|
||||
pydevd_comm.start_server = start_server
|
||||
pydevd_comm.start_client = start_client
|
||||
|
|
|
|||
7
setup.py
7
setup.py
|
|
@ -1,11 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
import sys
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
setup(name='ptvsd',
|
||||
version='4.0.0a1',
|
||||
description='Visual Studio remote debugging server for Python',
|
||||
|
|
@ -21,4 +22,4 @@ setup(name='ptvsd',
|
|||
'License :: OSI Approved :: MIT License'],
|
||||
packages=['ptvsd'],
|
||||
install_requires=['untangle', 'pydevd>=1.1.1']
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue