Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use code object instead of (co_firstlineno, co_name, co_filename) for caching frame info. Fixes #837 Fixes #844 #851

Merged
merged 1 commit into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
729 changes: 331 additions & 398 deletions src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_cython.c

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_cython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ cdef class PyDBFrame:
cdef str curr_func_name;
cdef bint exist_result;
cdef dict frame_skips_cache;
cdef tuple frame_cache_key;
cdef object frame_cache_key;
cdef tuple line_cache_key;
cdef int breakpoints_in_line_cache;
cdef int breakpoints_in_frame_cache;
Expand Down Expand Up @@ -1640,7 +1640,7 @@ cdef class ThreadTracer:
cdef str filename;
cdef str base;
cdef int pydev_step_cmd;
cdef tuple frame_cache_key;
cdef object frame_cache_key;
cdef dict cache_skips;
cdef bint is_stepping;
cdef tuple abs_path_canonical_path_and_base;
Expand All @@ -1667,7 +1667,7 @@ cdef class ThreadTracer:

# Note: it's important that the context name is also given because we may hit something once
# in the global context and another in the local context.
frame_cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, frame.f_code.co_filename)
frame_cache_key = frame.f_code
if frame_cache_key in cache_skips:
if not is_stepping:
# if DEBUG: print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
Expand All @@ -1681,7 +1681,7 @@ cdef class ThreadTracer:

back_frame = frame.f_back
if back_frame is not None and pydev_step_cmd in (107, 144, 109, 160):
back_frame_cache_key = (back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename)
back_frame_cache_key = back_frame.f_code
if cache_skips.get(back_frame_cache_key) == 1:
# if DEBUG: print('skipped: trace_dispatch (cache hit: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None if event == 'call' else NO_FTRACE
Expand Down Expand Up @@ -1721,7 +1721,7 @@ cdef class ThreadTracer:
back_frame = frame.f_back
if back_frame is not None and pydev_step_cmd in (107, 144, 109, 160):
if py_db.apply_files_filter(back_frame, back_frame.f_code.co_filename, False):
back_frame_cache_key = (back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename)
back_frame_cache_key = back_frame.f_code
cache_skips[back_frame_cache_key] = 1
# if DEBUG: print('skipped: trace_dispatch (filtered out: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None if event == 'call' else NO_FTRACE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ def _get_unfiltered_back_frame(self, main_debugger, frame):
# cdef str curr_func_name;
# cdef bint exist_result;
# cdef dict frame_skips_cache;
# cdef tuple frame_cache_key;
# cdef object frame_cache_key;
# cdef tuple line_cache_key;
# cdef int breakpoints_in_line_cache;
# cdef int breakpoints_in_frame_cache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def __call__(self, frame, event, arg):
# cdef str filename;
# cdef str base;
# cdef int pydev_step_cmd;
# cdef tuple frame_cache_key;
# cdef object frame_cache_key;
# cdef dict cache_skips;
# cdef bint is_stepping;
# cdef tuple abs_path_canonical_path_and_base;
Expand All @@ -367,7 +367,7 @@ def __call__(self, frame, event, arg):

# Note: it's important that the context name is also given because we may hit something once
# in the global context and another in the local context.
frame_cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, frame.f_code.co_filename)
frame_cache_key = frame.f_code
if frame_cache_key in cache_skips:
if not is_stepping:
# if DEBUG: print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
Expand All @@ -381,7 +381,7 @@ def __call__(self, frame, event, arg):

back_frame = frame.f_back
if back_frame is not None and pydev_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE):
back_frame_cache_key = (back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename)
back_frame_cache_key = back_frame.f_code
if cache_skips.get(back_frame_cache_key) == 1:
# if DEBUG: print('skipped: trace_dispatch (cache hit: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None if event == 'call' else NO_FTRACE
Expand Down Expand Up @@ -421,7 +421,7 @@ def __call__(self, frame, event, arg):
back_frame = frame.f_back
if back_frame is not None and pydev_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE):
if py_db.apply_files_filter(back_frame, back_frame.f_code.co_filename, False):
back_frame_cache_key = (back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename)
back_frame_cache_key = back_frame.f_code
cache_skips[back_frame_cache_key] = 1
# if DEBUG: print('skipped: trace_dispatch (filtered out: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None if event == 'call' else NO_FTRACE
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from _pydevd_bundle.pydevd_comm_constants import file_system_encoding
from _pydevd_bundle.pydevd_constants import (int_types, IS_64BIT_PROCESS,
PY_VERSION_STR, PY_IMPL_VERSION_STR, PY_IMPL_NAME, IS_PY36_OR_GREATER,
IS_PYPY, GENERATED_LEN_ATTR_NAME, IS_WINDOWS, IS_LINUX, IS_MAC)
IS_PYPY, GENERATED_LEN_ATTR_NAME, IS_WINDOWS, IS_LINUX, IS_MAC, IS_PY38_OR_GREATER)
from tests_python import debugger_unittest
from tests_python.debug_constants import TEST_CHERRYPY, IS_PY2, TEST_DJANGO, TEST_FLASK, IS_PY26, \
IS_PY27, IS_CPYTHON, TEST_GEVENT, TEST_CYTHON
Expand Down Expand Up @@ -5988,6 +5988,43 @@ def pandas_mod():
writer.finished_ok = True


@pytest.mark.skipif(not IS_PY38_OR_GREATER, reason='Python 3.8 onwards required for test.')
def test_same_lineno_and_filename(case_setup, pyfile):

@pyfile
def target():

def some_code():
print('1') # Break here

code_obj = compile('''
func()
''', __file__, 'exec')

code_obj = code_obj.replace(co_name=some_code.__code__.co_name, co_firstlineno=some_code.__code__.co_firstlineno)
exec(code_obj, {'func': some_code})

print('TEST SUCEEDED')

with case_setup.test_file(target) as writer:
json_facade = JsonFacade(writer)

writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
json_facade.write_launch(justMyCode=False)
json_facade.write_make_initial_run()

json_hit = json_facade.wait_for_thread_stopped()
json_facade.write_continue()

if sys.version_info[:2] >= (3, 10):
# On Python 3.10 we'll stop twice in this specific case
# because the line actually matches in the caller (so
# this is correct based on what the debugger is seeing...)
json_hit = json_facade.wait_for_thread_stopped()
json_facade.write_continue()
writer.finished_ok = True


if __name__ == '__main__':
pytest.main(['-k', 'test_case_skipping_filters', '-s'])

35 changes: 25 additions & 10 deletions src/debugpy/_vendored/pydevd/tests_python/test_run.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import pytest
pytest_plugins = [
str('_pytest.pytester'),
]


def _run_and_check(testdir, path, check_for='Worked'):
result = testdir.runpython(path)
def _run_and_check(testdir_or_pytester, path, check_for='Worked'):
result = testdir_or_pytester.runpython(path)
result.stdout.fnmatch_lines([
check_for
])

def test_run(testdir):

if hasattr(pytest, 'version_tuple') and pytest.version_tuple[0] >= 7:

@pytest.fixture
def testdir_or_pytester(pytester):
return pytester

else:

@pytest.fixture
def testdir_or_pytester(testdir):
return testdir


def test_run(testdir_or_pytester):
from tests_python import debugger_unittest
import sys
import os
Expand All @@ -24,7 +39,7 @@ def test_run(testdir):
pydevd_dir = os.path.dirname(os.path.dirname(__file__))
assert os.path.exists(os.path.join(pydevd_dir, 'pydevd.py'))

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
sys.path.append(%(pydevd_dir)r)
import pydevd
Expand All @@ -33,7 +48,7 @@ def test_run(testdir):
py_db.run(%(foo_dir)r)
''' % locals()))

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
sys.path.append(%(pydevd_dir)r)
import pydevd
Expand All @@ -45,7 +60,7 @@ def test_run(testdir):
# Not valid for Python 2.6
return

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
sys.path.append(%(pydevd_dir)r)
sys.argv.append('--as-module')
Expand All @@ -55,7 +70,7 @@ def test_run(testdir):
py_db.run(%(foo_module)r, is_module=True)
''' % locals()))

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
sys.argv.append('--as-module')
sys.path.append(%(pydevd_dir)r)
Expand All @@ -65,7 +80,7 @@ def test_run(testdir):
''' % locals()))


def test_run_on_local_module_without_adding_to_pythonpath(testdir):
def test_run_on_local_module_without_adding_to_pythonpath(testdir_or_pytester):
import sys
import os

Expand All @@ -76,7 +91,7 @@ def test_run_on_local_module_without_adding_to_pythonpath(testdir):
with open(os.path.join(os.getcwd(), 'local_foo.py'), 'w') as stream:
stream.write('print("WorkedLocalFoo")')

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
import os
sys.path.append(%(pydevd_dir)r)
Expand All @@ -90,7 +105,7 @@ def test_run_on_local_module_without_adding_to_pythonpath(testdir):
py_db.run(%(foo_module)r, is_module=True)
''' % locals()), check_for='WorkedLocalFoo')

_run_and_check(testdir, testdir.makepyfile('''
_run_and_check(testdir_or_pytester, testdir_or_pytester.makepyfile('''
import sys
import os
sys.argv.append('--as-module')
Expand Down