Skip to content

Commit bcc5156

Browse files
Don JayamanneDon Jayamanne
authored andcommitted
updated ptvsd scripts
1 parent 88556a9 commit bcc5156

6 files changed

Lines changed: 294 additions & 178 deletions

File tree

pythonFiles/PythonTools/ptvsd/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@
1919

2020
__all__ = ['enable_attach', 'wait_for_attach', 'break_into_debugger', 'settrace', 'is_attached', 'AttachAlreadyEnabledError']
2121

22-
from ptvsd.attach_server import enable_attach, wait_for_attach, break_into_debugger, settrace, is_attached, AttachAlreadyEnabledError
22+
from ptvsd.attach_server import enable_attach, wait_for_attach, break_into_debugger, settrace, is_attached, AttachAlreadyEnabledError

pythonFiles/PythonTools/ptvsd/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@
5454
DONT_DEBUG.append(os.path.normcase(__file__))
5555

5656
sys.argv = script_argv
57-
exec_file(script_argv[0], {'__name__': '__main__'})
57+
exec_file(script_argv[0], {'__name__': '__main__'})

pythonFiles/PythonTools/ptvsd/setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@
1818
from distutils.core import setup
1919

2020
setup(name='ptvsd',
21-
version='3.0.0rc1',
22-
description='Python Tools for Visual Studio remote debugging server',
21+
version='3.0.0',
22+
description='Visual Studio remote debugging server for Python',
2323
license='Apache License 2.0',
2424
author='Microsoft Corporation',
2525
author_email='ptvshelp@microsoft.com',
2626
url='https://aka.ms/ptvs',
2727
classifiers=[
28-
'Development Status :: 4 - Beta',
28+
'Development Status :: 5 - Production/Stable',
2929
'Programming Language :: Python',
3030
'Programming Language :: Python :: 2',
3131
'Programming Language :: Python :: 3',
3232
'License :: OSI Approved :: Apache Software License'],
3333
packages=['ptvsd']
34-
)
34+
)

pythonFiles/PythonTools/ptvsd/visualstudio_py_debugger.py

Lines changed: 129 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
# ############################################################################
2-
#
3-
# Copyright (c) Microsoft Corporation.
4-
#
5-
# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6-
# copy of the license can be found in the License.html file at the root of this distribution. If
7-
# you cannot locate the Apache License, Version 2.0, please send an email to
8-
# vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9-
# by the terms of the Apache License, Version 2.0.
10-
#
11-
# You must not remove this notice, or any other, from this software.
12-
#
13-
# ###########################################################################
1+
# Python Tools for Visual Studio
2+
# Copyright(c) Microsoft Corporation
3+
# All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
# this file except in compliance with the License. You may obtain a copy of the
7+
# License at http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
# OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
# IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
# MERCHANTABLITY OR NON-INFRINGEMENT.
13+
#
14+
# See the Apache Version 2.0 License for specific language governing
15+
# permissions and limitations under the License.
1416

1517
from __future__ import with_statement
1618

19+
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
20+
__version__ = "3.0.0.0"
21+
1722
# This module MUST NOT import threading in global scope. This is because in a direct (non-ptvsd)
1823
# attach scenario, it is loaded on the injected debugger attach thread, and if threading module
1924
# hasn't been loaded already, it will assume that the thread on which it is being loaded is the
@@ -105,7 +110,21 @@ def thread_creator(func, args, kwargs = {}, *extra_args):
105110

106111
return _start_new_thread(new_thread_wrapper, (func, args, kwargs))
107112

108-
_start_new_thread = thread.start_new_thread
113+
_thread_start_new_thread = thread.start_new_thread
114+
def _start_new_thread(func, args, kwargs = {}):
115+
t_lock = thread.allocate_lock()
116+
t_lock.acquire()
117+
118+
tid = []
119+
def thread_starter(a, kw):
120+
tid.append(thread.get_ident())
121+
t_lock.release()
122+
return func(*a, **kw)
123+
124+
_thread_start_new_thread(thread_starter, (args, kwargs))
125+
with t_lock:
126+
return tid[0]
127+
109128
THREADS = {}
110129
THREADS_LOCK = thread.allocate_lock()
111130
MODULES = []
@@ -114,6 +133,8 @@ def thread_creator(func, args, kwargs = {}, *extra_args):
114133
DEBUG_STDLIB = False
115134
DJANGO_DEBUG = False
116135

136+
RICH_EXCEPTIONS = False
137+
117138
# Py3k compat - alias unicode to str
118139
try:
119140
unicode
@@ -330,6 +351,7 @@ class StackOverflowException(Exception): pass
330351
EXTT = to_bytes('EXTT')
331352
EXIT = to_bytes('EXIT')
332353
EXCP = to_bytes('EXCP')
354+
EXC2 = to_bytes('EXC2')
333355
MODL = to_bytes('MODL')
334356
STPD = to_bytes('STPD')
335357
BRKS = to_bytes('BRKS')
@@ -366,12 +388,14 @@ def is_file_in_zip(filename):
366388
return False
367389
elif parent in KNOWN_ZIPS:
368390
return True
369-
elif path.isdir(parent):
391+
elif path.isfile(parent):
392+
KNOWN_ZIPS.add(parent)
393+
return True
394+
elif path.isdir(parent):
370395
KNOWN_DIRECTORIES.add(parent)
371396
return False
372397
else:
373-
KNOWN_ZIPS.add(parent)
374-
return True
398+
return is_file_in_zip(parent)
375399

376400
def lookup_builtin(name, frame):
377401
try:
@@ -455,8 +479,7 @@ def should_break(self, thread, ex_type, ex_value, trace):
455479
if break_type:
456480
if issubclass(ex_type, SystemExit):
457481
if not BREAK_ON_SYSTEMEXIT_ZERO:
458-
if ((isinstance(ex_value, int) and not ex_value) or
459-
(isinstance(ex_value, SystemExit) and not ex_value.code)):
482+
if not ex_value or (isinstance(ex_value, SystemExit) and not ex_value.code):
460483
break_type = BREAK_TYPE_NONE
461484

462485
return break_type
@@ -560,7 +583,7 @@ def should_debug_code(code):
560583

561584
return True
562585

563-
attach_lock = thread.allocate()
586+
attach_lock = thread.allocate_lock()
564587
attach_sent_break = False
565588

566589
local_path_to_vs_path = {}
@@ -642,9 +665,12 @@ def line_locations(self):
642665
line_info = []
643666
file_len = 0
644667
for line in contents:
668+
line_len = len(line)
645669
if not line_info and line.startswith(BOM_UTF8):
646-
line = line[3:] # Strip the BOM, Django seems to ignore this...
647-
file_len += len(line)
670+
line_len -= len(BOM_UTF8) # Strip the BOM, Django seems to ignore this...
671+
if line.endswith(to_bytes('\r\n')):
672+
line_len -= 1 # Django normalizes newlines to \n
673+
file_len += line_len
648674
line_info.append(file_len)
649675
contents.close()
650676
self._line_locations = line_info
@@ -675,10 +701,26 @@ def should_break(self, start, end):
675701
def get_django_frame_source(frame):
676702
if frame.f_code.co_name == 'render':
677703
self_obj = frame.f_locals.get('self', None)
678-
if self_obj is not None and type(self_obj).__name__ != 'TextNode':
679-
source_obj = getattr(self_obj, 'source', None)
680-
if source_obj is not None:
681-
return source_obj
704+
if self_obj is None:
705+
return None
706+
name = type(self_obj).__name__
707+
if name in ('Template', 'TextNode'):
708+
return None
709+
source_obj = getattr(self_obj, 'source', None)
710+
if source_obj and hasattr(source_obj, '__len__') and len(source_obj) == 2:
711+
return str(source_obj[0]), source_obj[1]
712+
713+
token_obj = getattr(self_obj, 'token', None)
714+
if token_obj is None:
715+
return None
716+
template_obj = getattr(frame.f_locals.get('context', None), 'template', None)
717+
if template_obj is None:
718+
return None
719+
template_name = getattr(template_obj, 'origin', None)
720+
position = getattr(token_obj, 'position', None)
721+
if template_name and position:
722+
return str(template_name), position
723+
682724

683725
return None
684726

@@ -879,8 +921,8 @@ def handle_call(self, frame, arg):
879921
source_obj = get_django_frame_source(frame)
880922
if source_obj is not None:
881923
origin, (start, end) = source_obj
882-
883-
active_bps = DJANGO_BREAKPOINTS.get(origin.name.lower())
924+
925+
active_bps = DJANGO_BREAKPOINTS.get(origin.lower())
884926
should_break = False
885927
if active_bps is not None:
886928
should_break, bkpt_id = active_bps.should_break(start, end)
@@ -1436,7 +1478,13 @@ def get_frame_list(self):
14361478
# collect globals used locally, skipping undefined found in builtins
14371479
f_globals = cur_frame.f_globals
14381480
if f_globals: # ensure globals to work with (IPy may have None for cur_frame.f_globals for frames within stdlib)
1439-
self.collect_variables(vars, f_globals, cur_frame.f_code.co_names, treated, skip_unknown = True)
1481+
self.collect_variables(
1482+
vars,
1483+
f_globals,
1484+
getattr(cur_frame.f_code, 'co_names', ()),
1485+
treated,
1486+
skip_unknown = True
1487+
)
14401488

14411489
frame_info = None
14421490

@@ -1582,10 +1630,11 @@ class DebuggerLoop(object):
15821630

15831631
instance = None
15841632

1585-
def __init__(self, conn):
1633+
def __init__(self, conn, rich_exceptions=False):
15861634
DebuggerLoop.instance = self
15871635
self.conn = conn
15881636
self.repl_backend = None
1637+
self.rich_exceptions = rich_exceptions
15891638
self.command_table = {
15901639
to_bytes('stpi') : self.command_step_into,
15911640
to_bytes('stpo') : self.command_step_out,
@@ -1599,6 +1648,7 @@ def __init__(self, conn):
15991648
to_bytes('brka') : self.command_break_all,
16001649
to_bytes('resa') : self.command_resume_all,
16011650
to_bytes('rest') : self.command_resume_thread,
1651+
to_bytes('thrf') : self.command_get_thread_frames,
16021652
to_bytes('ares') : self.command_auto_resume,
16031653
to_bytes('exec') : self.command_execute_code,
16041654
to_bytes('chld') : self.command_enum_children,
@@ -1782,6 +1832,13 @@ def command_break_all(self):
17821832
SEND_BREAK_COMPLETE = True
17831833
mark_all_threads_for_break()
17841834

1835+
def command_get_thread_frames(self):
1836+
tid = read_int(self.conn)
1837+
THREADS_LOCK.acquire()
1838+
thread = THREADS[tid]
1839+
THREADS_LOCK.release()
1840+
thread.enum_thread_frames_locally()
1841+
17851842
def command_resume_all(self):
17861843
# resume all
17871844
THREADS_LOCK.acquire()
@@ -1983,7 +2040,6 @@ def report_thread_exit(old_thread):
19832040

19842041
def report_exception(frame, exc_info, tid, break_type):
19852042
exc_type = exc_info[0]
1986-
exc_name = get_exception_name(exc_type)
19872043
exc_value = exc_info[1]
19882044
tb_value = exc_info[2]
19892045

@@ -1992,14 +2048,45 @@ def report_exception(frame, exc_info, tid, break_type):
19922048
# so we can get the correct msg.
19932049
exc_value = exc_type(*exc_value)
19942050

1995-
excp_text = str(exc_value)
2051+
data = {
2052+
'typename': get_exception_name(exc_type),
2053+
'message': str(exc_value),
2054+
}
2055+
if break_type == 1:
2056+
data['breaktype'] = 'unhandled'
2057+
if tb_value:
2058+
try:
2059+
data['trace'] = '\n'.join(','.join(repr(v) for v in line) for line in traceback.extract_tb(tb_value))
2060+
except:
2061+
pass
2062+
if not DJANGO_DEBUG or get_django_frame_source(frame) is None:
2063+
data['excvalue'] = '__exception_info'
2064+
i = 0
2065+
while data['excvalue'] in frame.f_locals:
2066+
i += 1
2067+
data['excvalue'] = '__exception_info_%d' % i
2068+
frame.f_locals[data['excvalue']] = {
2069+
'exception': exc_value,
2070+
'exception_type': exc_type,
2071+
'message': data['message'],
2072+
}
19962073

19972074
with _SendLockCtx:
1998-
write_bytes(conn, EXCP)
1999-
write_string(conn, exc_name)
2000-
write_int(conn, tid)
2001-
write_int(conn, break_type)
2002-
write_string(conn, excp_text)
2075+
if RICH_EXCEPTIONS:
2076+
write_bytes(conn, EXC2)
2077+
write_int(conn, tid)
2078+
write_int(conn, len(data))
2079+
for key, value in data.items():
2080+
write_string(conn, key)
2081+
write_string(conn, str(value))
2082+
else:
2083+
# Old message is fixed format. If RichExceptions is not passed in
2084+
# debug options, we'll send this format.
2085+
write_bytes(conn, EXCP)
2086+
write_string(conn, str(data['typename']))
2087+
write_int(conn, tid)
2088+
write_int(conn, 1 if 'breaktype' in data else 0)
2089+
write_string(conn, str(data['message']))
20032090

20042091
def new_module(frame):
20052092
mod = Module(get_code_filename(frame.f_code))
@@ -2172,6 +2259,7 @@ def attach_process(port_num, debug_id, debug_options, report = False, block = Fa
21722259

21732260
def attach_process_from_socket(sock, debug_options, report = False, block = False):
21742261
global conn, attach_sent_break, DETACHED, DEBUG_STDLIB, BREAK_ON_SYSTEMEXIT_ZERO, DJANGO_DEBUG
2262+
global RICH_EXCEPTIONS
21752263

21762264
BREAK_ON_SYSTEMEXIT_ZERO = 'BreakOnSystemExitZero' in debug_options
21772265
DJANGO_DEBUG = 'DjangoDebugging' in debug_options
@@ -2186,10 +2274,12 @@ def attach_process_from_socket(sock, debug_options, report = False, block = Fals
21862274
wait_on_normal_exit = 'WaitOnNormalExit' in debug_options
21872275
wait_on_abnormal_exit = 'WaitOnAbnormalExit' in debug_options
21882276

2277+
RICH_EXCEPTIONS = 'RichExceptions' in debug_options
2278+
21892279
def _excepthook(exc_type, exc_value, exc_tb):
21902280
# Display the exception and wait on exit
21912281
if exc_type is SystemExit:
2192-
if (wait_on_abnormal_exit and exc_value.code != 0) or (wait_on_normal_exit and exc_value.code == 0):
2282+
if (wait_on_abnormal_exit and exc_value.code) or (wait_on_normal_exit and not exc_value.code):
21932283
print_exception(exc_type, exc_value, exc_tb)
21942284
do_wait()
21952285
else:
@@ -2494,4 +2584,4 @@ def debug(file, port_num, debug_id, debug_options, run_as = 'script'):
24942584
get_code(exec_module),
24952585
get_code(exec_code),
24962586
get_code(new_thread_wrapper)
2497-
))
2587+
))

0 commit comments

Comments
 (0)