mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Don't trace <string> files unless it's the one from python -c. Fixes #1398
This commit is contained in:
parent
65c50f8769
commit
95c456d2e7
18 changed files with 2071 additions and 1730 deletions
|
|
@ -121,7 +121,7 @@ def stop_on_unhandled_exception(py_db, thread, additional_info, arg):
|
|||
|
||||
while tb:
|
||||
frame = tb.tb_frame
|
||||
if exception_breakpoint.ignore_libraries and py_db.in_project_scope(frame.f_code.co_filename):
|
||||
if exception_breakpoint.ignore_libraries and py_db.in_project_scope(frame):
|
||||
user_frame = tb.tb_frame
|
||||
frames.append(tb.tb_frame)
|
||||
tb = tb.tb_next
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -793,8 +793,7 @@ cdef class PyDBFrame:
|
|||
stop = True
|
||||
|
||||
elif is_return and frame.f_back is not None:
|
||||
if main_debugger.get_file_type(
|
||||
get_abs_path_real_path_and_base_from_frame(frame.f_back)) == main_debugger.PYDEV_FILE:
|
||||
if main_debugger.get_file_type(frame.f_back) == main_debugger.PYDEV_FILE:
|
||||
stop = False
|
||||
else:
|
||||
if force_check_project_scope or main_debugger.is_files_filter_enabled:
|
||||
|
|
@ -849,8 +848,7 @@ cdef class PyDBFrame:
|
|||
if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"):
|
||||
f_code = getattr(frame.f_back, 'f_code', None)
|
||||
if f_code is not None:
|
||||
if main_debugger.get_file_type(
|
||||
get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE:
|
||||
if main_debugger.get_file_type(frame.f_back) == main_debugger.PYDEV_FILE:
|
||||
stop = False
|
||||
|
||||
if plugin_stop:
|
||||
|
|
@ -1347,11 +1345,11 @@ cdef class ThreadTracer:
|
|||
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
filename = abs_path_real_path_and_base[1]
|
||||
file_type = py_db.get_file_type(abs_path_real_path_and_base) # we don't want to debug threading or anything related to pydevd
|
||||
file_type = py_db.get_file_type(frame, abs_path_real_path_and_base) # we don't want to debug threading or anything related to pydevd
|
||||
|
||||
if file_type is not None:
|
||||
if file_type == 1: # inlining LIB_FILE = 1
|
||||
if not py_db.in_project_scope(filename):
|
||||
if not py_db.in_project_scope(frame, abs_path_real_path_and_base[0]):
|
||||
# if DEBUG: print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
|
||||
cache_skips[frame_cache_key] = 1
|
||||
return None if event == 'call' else NO_FTRACE
|
||||
|
|
|
|||
|
|
@ -643,8 +643,7 @@ class PyDBFrame:
|
|||
stop = True
|
||||
|
||||
elif is_return and frame.f_back is not None:
|
||||
if main_debugger.get_file_type(
|
||||
get_abs_path_real_path_and_base_from_frame(frame.f_back)) == main_debugger.PYDEV_FILE:
|
||||
if main_debugger.get_file_type(frame.f_back) == main_debugger.PYDEV_FILE:
|
||||
stop = False
|
||||
else:
|
||||
if force_check_project_scope or main_debugger.is_files_filter_enabled:
|
||||
|
|
@ -699,8 +698,7 @@ class PyDBFrame:
|
|||
if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"):
|
||||
f_code = getattr(frame.f_back, 'f_code', None)
|
||||
if f_code is not None:
|
||||
if main_debugger.get_file_type(
|
||||
get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE:
|
||||
if main_debugger.get_file_type(frame.f_back) == main_debugger.PYDEV_FILE:
|
||||
stop = False
|
||||
|
||||
if plugin_stop:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from _pydevd_bundle.pydevd_constants import IS_PY3K
|
||||
|
||||
|
||||
class Frame(object):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
f_back,
|
||||
|
|
@ -21,9 +23,11 @@ class Frame(object):
|
|||
|
||||
|
||||
class FCode(object):
|
||||
|
||||
def __init__(self, name, filename):
|
||||
self.co_name = name
|
||||
self.co_filename = filename
|
||||
self.co_firstlineno = 1
|
||||
|
||||
|
||||
def add_exception_to_frame(frame, exception_info):
|
||||
|
|
@ -36,11 +40,13 @@ def remove_exception_from_frame(frame):
|
|||
|
||||
FILES_WITH_IMPORT_HOOKS = ['pydev_monkey_qt.py', 'pydev_import_hook.py']
|
||||
|
||||
|
||||
def just_raised(trace):
|
||||
if trace is None:
|
||||
return False
|
||||
return trace.tb_next is None
|
||||
|
||||
|
||||
def ignore_exception_trace(trace):
|
||||
while trace is not None:
|
||||
filename = trace.tb_frame.f_code.co_filename
|
||||
|
|
@ -58,6 +64,7 @@ def ignore_exception_trace(trace):
|
|||
|
||||
return False
|
||||
|
||||
|
||||
def cached_call(obj, func, *args):
|
||||
cached_name = '_cached_' + func.__name__
|
||||
if not hasattr(obj, cached_name):
|
||||
|
|
@ -65,4 +72,3 @@ def cached_call(obj, func, *args):
|
|||
|
||||
return getattr(obj, cached_name)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
if py_db.is_files_filter_enabled and py_db.apply_files_filter(frame, original_filename, False):
|
||||
continue
|
||||
|
||||
if not py_db.in_project_scope(original_filename):
|
||||
if not py_db.in_project_scope(frame):
|
||||
presentation_hint = 'subtle'
|
||||
|
||||
formatted_name = self._format_frame_name(fmt, method_name, module_name, lineno, filename_in_utf8)
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class NetCommandFactory(object):
|
|||
continue # IronPython sometimes does not have it!
|
||||
|
||||
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
if py_db.get_file_type(abs_path_real_path_and_base) == py_db.PYDEV_FILE:
|
||||
if py_db.get_file_type(frame, abs_path_real_path_and_base) == py_db.PYDEV_FILE:
|
||||
# Skip pydevd files.
|
||||
frame = frame.f_back
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ def create_signature_message(signature):
|
|||
|
||||
|
||||
def send_signature_call_trace(dbg, frame, filename):
|
||||
if dbg.signature_factory and dbg.in_project_scope(filename):
|
||||
if dbg.signature_factory and dbg.in_project_scope(frame):
|
||||
signature = dbg.signature_factory.create_signature(frame, filename)
|
||||
if signature is not None:
|
||||
if dbg.signature_factory.cache is not None:
|
||||
|
|
@ -192,7 +192,7 @@ def send_signature_call_trace(dbg, frame, filename):
|
|||
|
||||
|
||||
def send_signature_return_trace(dbg, frame, filename, return_value):
|
||||
if dbg.signature_factory and dbg.in_project_scope(filename):
|
||||
if dbg.signature_factory and dbg.in_project_scope(frame):
|
||||
signature = dbg.signature_factory.create_signature(frame, filename, with_args=False)
|
||||
signature.return_type = get_type_of_value(return_value, recursive=True)
|
||||
dbg.writer.add_command(create_signature_message(signature))
|
||||
|
|
|
|||
|
|
@ -222,9 +222,8 @@ def _schedule_callback(prev, next):
|
|||
if frame is current_frame:
|
||||
frame = frame.f_back
|
||||
if frame is not None:
|
||||
abs_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
# print >>sys.stderr, "SchedCB: %r, %d, '%s', '%s'" % (tasklet, frame.f_lineno, _filename, base)
|
||||
if debugger.get_file_type(abs_real_path_and_base) is None:
|
||||
if debugger.get_file_type(frame) is None:
|
||||
tasklet_info.update_name()
|
||||
if tasklet_info.frame_id is None:
|
||||
tasklet_info.frame_id = add_custom_frame(frame, tasklet_info.tasklet_name, tasklet.thread_id)
|
||||
|
|
@ -290,8 +289,7 @@ if not hasattr(stackless.tasklet, "trace_function"):
|
|||
if tasklet.paused or tasklet.blocked or tasklet.scheduled:
|
||||
if tasklet.frame and tasklet.frame.f_back:
|
||||
f_back = tasklet.frame.f_back
|
||||
abs_real_path_and_base = get_abs_path_real_path_and_base_from_frame(f_back)
|
||||
if debugger.get_file_type(abs_real_path_and_base) is None:
|
||||
if debugger.get_file_type(f_back) is None:
|
||||
if tasklet_info.frame_id is None:
|
||||
tasklet_info.frame_id = add_custom_frame(f_back, tasklet_info.tasklet_name, tasklet.thread_id)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -437,11 +437,11 @@ class ThreadTracer(object):
|
|||
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
filename = abs_path_real_path_and_base[1]
|
||||
file_type = py_db.get_file_type(abs_path_real_path_and_base) # we don't want to debug threading or anything related to pydevd
|
||||
file_type = py_db.get_file_type(frame, abs_path_real_path_and_base) # we don't want to debug threading or anything related to pydevd
|
||||
|
||||
if file_type is not None:
|
||||
if file_type == 1: # inlining LIB_FILE = 1
|
||||
if not py_db.in_project_scope(filename):
|
||||
if not py_db.in_project_scope(frame, abs_path_real_path_and_base[0]):
|
||||
# if DEBUG: print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
|
||||
cache_skips[frame_cache_key] = 1
|
||||
return None if event == 'call' else NO_FTRACE
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -140,16 +140,16 @@ def decref_py(obj):
|
|||
Py_DECREF(obj)
|
||||
|
||||
|
||||
def get_func_code_info_py(code_obj) -> FuncCodeInfo:
|
||||
def get_func_code_info_py(frame, code_obj) -> FuncCodeInfo:
|
||||
'''
|
||||
Helper to be called from Python.
|
||||
'''
|
||||
return get_func_code_info(<PyCodeObject *> code_obj)
|
||||
return get_func_code_info(<PyFrameObject *> frame, <PyCodeObject *> code_obj)
|
||||
|
||||
|
||||
_code_extra_index: Py_SIZE = -1
|
||||
|
||||
cdef FuncCodeInfo get_func_code_info(PyCodeObject * code_obj):
|
||||
cdef FuncCodeInfo get_func_code_info(PyFrameObject * frame_obj, PyCodeObject * code_obj):
|
||||
'''
|
||||
Provides code-object related info.
|
||||
|
||||
|
|
@ -181,6 +181,8 @@ cdef FuncCodeInfo get_func_code_info(PyCodeObject * code_obj):
|
|||
cdef str co_filename = <str> code_obj.co_filename
|
||||
cdef str co_name = <str> code_obj.co_name
|
||||
cdef set break_at_lines
|
||||
cdef dict cache_file_type
|
||||
cdef tuple cache_file_type_key
|
||||
|
||||
func_code_info = FuncCodeInfo()
|
||||
func_code_info.breakpoints_mtime = main_debugger.mtime
|
||||
|
|
@ -194,8 +196,16 @@ cdef FuncCodeInfo get_func_code_info(PyCodeObject * code_obj):
|
|||
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file(co_filename)
|
||||
|
||||
func_code_info.real_path = abs_path_real_path_and_base[1]
|
||||
|
||||
cache_file_type = main_debugger.get_cache_file_type()
|
||||
# Note: this cache key must be the same from PyDB.get_file_type() -- see it for comments
|
||||
# on the cache.
|
||||
cache_file_type_key = (frame_obj.f_code.co_firstlineno, abs_path_real_path_and_base[0], <object>frame_obj.f_code)
|
||||
try:
|
||||
file_type = cache_file_type[cache_file_type_key] # Make it faster
|
||||
except:
|
||||
file_type = main_debugger.get_file_type(<object>frame_obj, abs_path_real_path_and_base) # we don't want to debug anything related to pydevd
|
||||
|
||||
file_type = main_debugger.get_file_type(abs_path_real_path_and_base) # we don't want to debug anything related to pydevd
|
||||
if file_type is not None:
|
||||
func_code_info.always_skip_code = True
|
||||
|
||||
|
|
@ -311,7 +321,7 @@ cdef PyObject * get_bytecode_while_frame_eval(PyFrameObject * frame_obj, int exc
|
|||
else:
|
||||
frame.f_trace = <object> main_debugger.trace_dispatch
|
||||
else:
|
||||
func_code_info: FuncCodeInfo = get_func_code_info(frame_obj.f_code)
|
||||
func_code_info: FuncCodeInfo = get_func_code_info(frame_obj, frame_obj.f_code)
|
||||
# if DEBUG:
|
||||
# print('get_bytecode_while_frame_eval always skip', func_code_info.always_skip_code)
|
||||
if not func_code_info.always_skip_code:
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ from _pydevd_bundle.pydevd_constants import (IS_JYTH_LESS25, get_thread_id, get_
|
|||
NO_FTRACE, IS_IRONPYTHON, JSON_PROTOCOL, IS_CPYTHON)
|
||||
from _pydevd_bundle.pydevd_defaults import PydevdCustomization
|
||||
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
|
||||
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE
|
||||
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE, LIB_FILE
|
||||
from _pydevd_bundle.pydevd_extension_api import DebuggerEventHandler
|
||||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, remove_exception_from_frame
|
||||
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
|
||||
|
|
@ -521,6 +521,7 @@ class PyDB(object):
|
|||
self.get_exception_breakpoint = get_exception_breakpoint
|
||||
self._dont_trace_get_file_type = DONT_TRACE.get
|
||||
self.PYDEV_FILE = PYDEV_FILE
|
||||
self.LIB_FILE = LIB_FILE
|
||||
|
||||
self._in_project_scope_cache = {}
|
||||
self._exclude_by_filter_cache = {}
|
||||
|
|
@ -632,7 +633,7 @@ class PyDB(object):
|
|||
# be changed for another function in PyDevdAPI.set_dont_trace_start_end_patterns.
|
||||
return False
|
||||
|
||||
def get_file_type(self, abs_real_path_and_basename, _cache_file_type=_CACHE_FILE_TYPE):
|
||||
def get_file_type(self, frame, abs_real_path_and_basename=None, _cache_file_type=_CACHE_FILE_TYPE):
|
||||
'''
|
||||
:param abs_real_path_and_basename:
|
||||
The result from get_abs_path_real_path_and_base_from_file or
|
||||
|
|
@ -649,18 +650,66 @@ class PyDB(object):
|
|||
None:
|
||||
If it's a regular user file which should be traced.
|
||||
'''
|
||||
if abs_real_path_and_basename is None:
|
||||
try:
|
||||
# Make fast path faster!
|
||||
abs_real_path_and_basename = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
|
||||
except:
|
||||
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
# Note 1: we have to take into account that we may have files as '<string>', and that in
|
||||
# this case the cache key can't rely only on the filename. With the current cache, there's
|
||||
# still a potential miss if 2 functions which have exactly the same content are compiled
|
||||
# with '<string>', but in practice as we only separate the one from python -c from the rest
|
||||
# this shouldn't be a problem in practice.
|
||||
|
||||
# Note 2: firstlineno added to make misses faster in the first comparison.
|
||||
|
||||
# Note 3: this cache key is repeated in pydevd_frame_evaluator.pyx:get_func_code_info (for
|
||||
# speedups).
|
||||
cache_key = (frame.f_code.co_firstlineno, abs_real_path_and_basename[0], frame.f_code)
|
||||
try:
|
||||
return _cache_file_type[abs_real_path_and_basename[0]]
|
||||
return _cache_file_type[cache_key]
|
||||
except:
|
||||
if abs_real_path_and_basename[0] == '<string>':
|
||||
|
||||
# Consider it an untraceable file unless there's no back frame (ignoring
|
||||
# internal files and runpy.py).
|
||||
f = frame.f_back
|
||||
while f is not None:
|
||||
if (self.get_file_type(f) != self.PYDEV_FILE and
|
||||
get_abs_path_real_path_and_base_from_file(f.f_code.co_filename)[2] != 'runpy.py'):
|
||||
# We found some back frame that's not internal, which means we must consider
|
||||
# this a library file.
|
||||
# This is done because we only want to trace files as <string> if they don't
|
||||
# have any back frame (which is the case for python -c ...), for all other
|
||||
# cases we don't want to trace them because we can't show the source to the
|
||||
# user (at least for now...).
|
||||
|
||||
# Note that we return as a LIB_FILE and not PYDEV_FILE because we still want
|
||||
# to show it in the stack.
|
||||
_cache_file_type[cache_key] = LIB_FILE
|
||||
return LIB_FILE
|
||||
f = f.f_back
|
||||
else:
|
||||
# This is a top-level file (used in python -c), so, trace it as usual... we
|
||||
# still won't be able to show the sources, but some tests require this to work.
|
||||
_cache_file_type[cache_key] = None
|
||||
return None
|
||||
|
||||
file_type = self._internal_get_file_type(abs_real_path_and_basename)
|
||||
if file_type is None:
|
||||
file_type = PYDEV_FILE if self.dont_trace_external_files(abs_real_path_and_basename[0]) else None
|
||||
_cache_file_type[abs_real_path_and_basename[0]] = file_type
|
||||
if self.dont_trace_external_files(abs_real_path_and_basename[0]):
|
||||
file_type = PYDEV_FILE
|
||||
_cache_file_type[cache_key] = file_type
|
||||
return file_type
|
||||
|
||||
def is_cache_file_type_empty(self):
|
||||
return not _CACHE_FILE_TYPE
|
||||
|
||||
def get_cache_file_type(self, _cache=_CACHE_FILE_TYPE): # i.e.: Make it local.
|
||||
return _cache
|
||||
|
||||
def get_thread_local_trace_func(self):
|
||||
try:
|
||||
thread_trace_func = self._local_thread_trace_func.thread_trace_func
|
||||
|
|
@ -681,7 +730,7 @@ class PyDB(object):
|
|||
If True we'll set the tracing function in all threads, not only in the current thread.
|
||||
If False only the tracing for the current function should be changed.
|
||||
In general apply_to_all_threads should only be true if this is the first time
|
||||
this function is called on a multi-threaded program (either programatically or attach
|
||||
this function is called on a multi-threaded program (either programmatically or attach
|
||||
to pid).
|
||||
'''
|
||||
if self.frame_eval_func is not None:
|
||||
|
|
@ -765,23 +814,54 @@ class PyDB(object):
|
|||
self.plugin = PluginManager(self)
|
||||
return self.plugin
|
||||
|
||||
def in_project_scope(self, filename):
|
||||
def in_project_scope(self, frame, filename=None):
|
||||
'''
|
||||
Note: in general this method should not be used (apply_files_filter should be used
|
||||
in most cases as it also handles the project scope check).
|
||||
|
||||
:param frame:
|
||||
The frame we want to check.
|
||||
|
||||
:param filename:
|
||||
Must be the result from get_abs_path_real_path_and_base_from_frame(frame)[0] (can
|
||||
be used to speed this function a bit if it's already available to the caller, but
|
||||
in general it's not needed).
|
||||
'''
|
||||
try:
|
||||
return self._in_project_scope_cache[filename]
|
||||
if filename is None:
|
||||
try:
|
||||
# Make fast path faster!
|
||||
abs_real_path_and_basename = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
|
||||
except:
|
||||
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
filename = abs_real_path_and_basename[0]
|
||||
|
||||
cache_key = (frame.f_code.co_firstlineno, filename, frame.f_code)
|
||||
|
||||
return self._in_project_scope_cache[cache_key]
|
||||
except KeyError:
|
||||
cache = self._in_project_scope_cache
|
||||
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_file(filename)
|
||||
# pydevd files are never considered to be in the project scope.
|
||||
if self.get_file_type(abs_real_path_and_basename) == self.PYDEV_FILE:
|
||||
cache[filename] = False
|
||||
else:
|
||||
cache[filename] = self._files_filtering.in_project_roots(filename)
|
||||
try:
|
||||
abs_real_path_and_basename # If we've gotten it previously, use it again.
|
||||
except NameError:
|
||||
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
return cache[filename]
|
||||
# pydevd files are never considered to be in the project scope.
|
||||
file_type = self.get_file_type(frame, abs_real_path_and_basename)
|
||||
if file_type == self.PYDEV_FILE:
|
||||
cache[cache_key] = False
|
||||
|
||||
elif file_type == self.LIB_FILE and filename == '<string>':
|
||||
# This means it's a <string> which should be considered to be a library file and
|
||||
# shouldn't be considered as a part of the project.
|
||||
# (i.e.: lib files must be traced if they're put inside a project).
|
||||
cache[cache_key] = False
|
||||
|
||||
else:
|
||||
cache[cache_key] = self._files_filtering.in_project_roots(filename)
|
||||
|
||||
return cache[cache_key]
|
||||
|
||||
def _clear_filters_caches(self):
|
||||
self._in_project_scope_cache.clear()
|
||||
|
|
@ -814,9 +894,8 @@ class PyDB(object):
|
|||
except KeyError:
|
||||
cache = self._exclude_by_filter_cache
|
||||
|
||||
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_file(filename)
|
||||
# pydevd files are always filtered out
|
||||
if self.get_file_type(abs_real_path_and_basename) == self.PYDEV_FILE:
|
||||
if self.get_file_type(frame) == self.PYDEV_FILE:
|
||||
cache[cache_key] = True
|
||||
else:
|
||||
module_name = None
|
||||
|
|
@ -841,7 +920,7 @@ class PyDB(object):
|
|||
True if it should be excluded when stepping and False if it should be
|
||||
included.
|
||||
'''
|
||||
cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, filename, force_check_project_scope)
|
||||
cache_key = (frame.f_code.co_firstlineno, filename, force_check_project_scope, frame.f_code)
|
||||
try:
|
||||
return self._apply_filter_cache[cache_key]
|
||||
except KeyError:
|
||||
|
|
@ -867,7 +946,7 @@ class PyDB(object):
|
|||
self._apply_filter_cache[cache_key] = False
|
||||
return False
|
||||
|
||||
if (self._is_libraries_filter_enabled or force_check_project_scope) and not self.in_project_scope(filename):
|
||||
if (self._is_libraries_filter_enabled or force_check_project_scope) and not self.in_project_scope(frame):
|
||||
# ignore library files while stepping
|
||||
self._apply_filter_cache[cache_key] = True
|
||||
if force_check_project_scope:
|
||||
|
|
@ -899,7 +978,7 @@ class PyDB(object):
|
|||
ignore_libraries = exception_breakpoint.ignore_libraries
|
||||
exclude_filters_enabled = self._exclude_filters_enabled
|
||||
|
||||
if (ignore_libraries and not self.in_project_scope(trace.tb_frame.f_code.co_filename)) \
|
||||
if (ignore_libraries and not self.in_project_scope(trace.tb_frame)) \
|
||||
or (exclude_filters_enabled and self._exclude_by_filter(trace.tb_frame, trace.tb_frame.f_code.co_filename)):
|
||||
return True
|
||||
|
||||
|
|
@ -1652,14 +1731,8 @@ class PyDB(object):
|
|||
assert not kwargs
|
||||
|
||||
while frame is not None:
|
||||
try:
|
||||
# Make fast path faster!
|
||||
abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
|
||||
except:
|
||||
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
|
||||
|
||||
# Don't change the tracing on debugger-related files
|
||||
file_type = self.get_file_type(abs_path_real_path_and_base)
|
||||
file_type = self.get_file_type(frame)
|
||||
|
||||
if file_type is None:
|
||||
if disable:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
from collections import namedtuple
|
||||
MyTup = namedtuple('MyTup', 'a, b, c')
|
||||
tup = MyTup(1, 2, 3) # break here
|
||||
assert tup.a == 1
|
||||
assert tup.b == 2
|
||||
print('TEST SUCEEDED!')
|
||||
|
|
@ -310,8 +310,8 @@ def test_source_mapping():
|
|||
from _pydevd_bundle.pydevd_source_mapping import SourceMapping, SourceMappingEntry
|
||||
source_mapping = SourceMapping()
|
||||
mapping = [
|
||||
SourceMappingEntry(source_filename='file1.py', line=3, end_line=6, runtime_line=5, runtime_source='<cell1>'),
|
||||
SourceMappingEntry(source_filename='file1.py', line=10, end_line=11, runtime_line=1, runtime_source='<cell2>'),
|
||||
SourceMappingEntry(line=3, end_line=6, runtime_line=5, runtime_source='<cell1>'),
|
||||
SourceMappingEntry(line=10, end_line=11, runtime_line=1, runtime_source='<cell2>'),
|
||||
]
|
||||
source_mapping.set_source_mapping('file1.py', mapping)
|
||||
|
||||
|
|
|
|||
|
|
@ -3307,6 +3307,29 @@ def test_step_over_my_code_global_setting_and_explicit_include(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_namedtuple(case_setup):
|
||||
'''
|
||||
Check that we don't step into <string> in the namedtuple constructor.
|
||||
'''
|
||||
with case_setup.test_file('_debugger_case_namedtuple.py') as writer:
|
||||
line = writer.get_line_index_with_content('break here')
|
||||
writer.write_add_breakpoint(line)
|
||||
writer.write_make_initial_run()
|
||||
|
||||
hit = writer.wait_for_breakpoint_hit()
|
||||
|
||||
expected_line = line
|
||||
|
||||
for _ in range(2):
|
||||
expected_line += 1
|
||||
writer.write_step_in(hit.thread_id)
|
||||
hit = writer.wait_for_breakpoint_hit(
|
||||
reason=REASON_STEP_INTO, file='_debugger_case_namedtuple.py', line=expected_line)
|
||||
|
||||
writer.write_run_thread(hit.thread_id)
|
||||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_matplotlib_activation(case_setup):
|
||||
try:
|
||||
import matplotlib
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def test_thread_info(_times):
|
|||
|
||||
|
||||
def method():
|
||||
pass
|
||||
return sys._getframe()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -60,17 +60,17 @@ def test_func_code_info(_times, _custom_global_dbg):
|
|||
# Must be called before get_func_code_info_py to initialize the _code_extra_index.
|
||||
pydevd_frame_evaluator.get_thread_info_py()
|
||||
|
||||
func_info = pydevd_frame_evaluator.get_func_code_info_py(method.__code__)
|
||||
func_info = pydevd_frame_evaluator.get_func_code_info_py(method(), method.__code__)
|
||||
assert func_info.co_filename is method.__code__.co_filename
|
||||
func_info2 = pydevd_frame_evaluator.get_func_code_info_py(method.__code__)
|
||||
func_info2 = pydevd_frame_evaluator.get_func_code_info_py(method(), method.__code__)
|
||||
assert func_info is func_info2
|
||||
|
||||
some_func = eval('lambda:0')
|
||||
func_info3 = pydevd_frame_evaluator.get_func_code_info_py(some_func.__code__)
|
||||
some_func = eval('lambda:sys._getframe()')
|
||||
func_info3 = pydevd_frame_evaluator.get_func_code_info_py(some_func(), some_func.__code__)
|
||||
del some_func
|
||||
del func_info3
|
||||
|
||||
some_func = eval('lambda:0')
|
||||
pydevd_frame_evaluator.get_func_code_info_py(some_func.__code__)
|
||||
func_info = pydevd_frame_evaluator.get_func_code_info_py(some_func.__code__)
|
||||
assert pydevd_frame_evaluator.get_func_code_info_py(some_func.__code__) is func_info
|
||||
some_func = eval('lambda:sys._getframe()')
|
||||
pydevd_frame_evaluator.get_func_code_info_py(some_func(), some_func.__code__)
|
||||
func_info = pydevd_frame_evaluator.get_func_code_info_py(some_func(), some_func.__code__)
|
||||
assert pydevd_frame_evaluator.get_func_code_info_py(some_func(), some_func.__code__) is func_info
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ from ptvsd.wrapper import debugger_attached
|
|||
|
||||
import pydevd
|
||||
from _pydevd_bundle.pydevd_constants import get_global_debugger
|
||||
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
|
||||
|
||||
WAIT_TIMEOUT = 1.0
|
||||
|
||||
|
|
@ -28,6 +27,7 @@ _redirect_output_deprecation_msg = (
|
|||
"This can be set using redirectOutput in Launch config in VS Code, using Tee output option in Visual Studio, "
|
||||
"or debugOptions configuration for any client.")
|
||||
|
||||
|
||||
def wait_for_attach(timeout=None):
|
||||
"""If a remote debugger is attached, returns immediately. Otherwise,
|
||||
blocks until a remote debugger attaches to this process, or until the
|
||||
|
|
@ -54,7 +54,7 @@ def enable_attach(address=(DEFAULT_HOST, DEFAULT_PORT), redirect_output=None, lo
|
|||
``(hostname, port)``. On client side, the server is identified by the
|
||||
Qualifier string in the usual ``'hostname:port'`` format, e.g.:
|
||||
``'myhost.cloudapp.net:5678'``. Default is ``('0.0.0.0', 5678)``.
|
||||
redirect_output : bool, optional
|
||||
redirect_output : bool, optional
|
||||
(Deprecated) Specifies whether any output (on both `stdout` and `stderr`) produced
|
||||
by this program should be sent to the debugger. Default is ``True``.
|
||||
log_dir : str, optional
|
||||
|
|
@ -162,10 +162,9 @@ def break_into_debugger():
|
|||
global_debugger = get_global_debugger()
|
||||
stop_at_frame = sys._getframe().f_back
|
||||
while stop_at_frame is not None and global_debugger.get_file_type(
|
||||
get_abs_path_real_path_and_base_from_frame(stop_at_frame)) == global_debugger.PYDEV_FILE:
|
||||
stop_at_frame) == global_debugger.PYDEV_FILE:
|
||||
stop_at_frame = stop_at_frame.f_back
|
||||
|
||||
|
||||
# pydevd.settrace() only enables debugging of the current
|
||||
# thread and all future threads. PyDevd is not enabled for
|
||||
# existing threads (other than the current one). Consequently,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue