Fix linting errors.

This commit is contained in:
Eric Snow 2018-01-09 20:10:54 +00:00
parent 4cdd5f5622
commit 4095e94e6d
10 changed files with 369 additions and 143 deletions

View file

@ -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"

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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])

View file

@ -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

View file

@ -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']
)
)