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

This commit is contained in:
Fabio Zadrozny 2022-02-24 16:07:01 -03:00
parent 1b8d5ab0be
commit 6134532263
7 changed files with 423 additions and 432 deletions

File diff suppressed because it is too large Load diff

View file

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

View file

@ -544,7 +544,7 @@ 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;

View file

@ -340,7 +340,7 @@ class ThreadTracer(object):
# 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;
@ -367,7 +367,7 @@ class ThreadTracer(object):
# 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)
@ -381,7 +381,7 @@ class ThreadTracer(object):
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
@ -421,7 +421,7 @@ class ThreadTracer(object):
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

View file

@ -1,4 +1,4 @@
/* Generated by Cython 0.29.26 */
/* Generated by Cython 0.29.28 */
/* BEGIN: Cython Metadata
{
@ -32,8 +32,8 @@ END: Cython Metadata */
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
#error Cython requires Python 2.6+ or Python 3.3+.
#else
#define CYTHON_ABI "0_29_26"
#define CYTHON_HEX_VERSION 0x001D1AF0
#define CYTHON_ABI "0_29_28"
#define CYTHON_HEX_VERSION 0x001D1CF0
#define CYTHON_FUTURE_DIVISION 0
#include <stddef.h>
#ifndef offsetof
@ -195,7 +195,10 @@ END: Cython Metadata */
#ifndef CYTHON_UNPACK_METHODS
#define CYTHON_UNPACK_METHODS 1
#endif
#ifndef CYTHON_FAST_THREAD_STATE
#if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
#elif !defined(CYTHON_FAST_THREAD_STATE)
#define CYTHON_FAST_THREAD_STATE 1
#endif
#ifndef CYTHON_FAST_PYCALL
@ -210,7 +213,10 @@ END: Cython Metadata */
#ifndef CYTHON_USE_DICT_VERSIONS
#define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1)
#endif
#ifndef CYTHON_USE_EXC_INFO_STACK
#if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0
#elif !defined(CYTHON_USE_EXC_INFO_STACK)
#define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3)
#endif
#endif
@ -15236,13 +15242,13 @@ static PyTypeObject __pyx_type_18_pydevd_frame_eval_22pydevd_frame_evaluator_Thr
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
#if PY_VERSION_HEX >= 0x030800b1
#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
0, /*tp_vectorcall*/
#endif
#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
0, /*tp_print*/
#endif
#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM+0 >= 0x06000000
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
0, /*tp_pypy_flags*/
#endif
};
@ -15454,13 +15460,13 @@ static PyTypeObject __pyx_type_18_pydevd_frame_eval_22pydevd_frame_evaluator_Fun
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
#if PY_VERSION_HEX >= 0x030800b1
#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
0, /*tp_vectorcall*/
#endif
#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
0, /*tp_print*/
#endif
#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM+0 >= 0x06000000
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
0, /*tp_pypy_flags*/
#endif
};
@ -15624,13 +15630,13 @@ static PyTypeObject __pyx_type_18_pydevd_frame_eval_22pydevd_frame_evaluator__Co
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
#if PY_VERSION_HEX >= 0x030800b1
#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
0, /*tp_vectorcall*/
#endif
#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
0, /*tp_print*/
#endif
#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM+0 >= 0x06000000
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
0, /*tp_pypy_flags*/
#endif
};
@ -15833,13 +15839,13 @@ static PyTypeObject __pyx_type_18_pydevd_frame_eval_22pydevd_frame_evaluator__Ca
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
#if PY_VERSION_HEX >= 0x030800b1
#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
0, /*tp_vectorcall*/
#endif
#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
0, /*tp_print*/
#endif
#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM+0 >= 0x06000000
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
0, /*tp_pypy_flags*/
#endif
};

View file

@ -19,7 +19,7 @@ from _pydevd_bundle._debug_adapter.pydevd_schema import (ThreadEvent, ModuleEven
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
@ -5988,6 +5988,43 @@ def test_pandas(case_setup, pyfile):
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'])

View file

@ -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
@ -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
@ -33,7 +48,7 @@ py_db.ready_to_run = True
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
@ -45,7 +60,7 @@ py_db.run(%(foo_dir)r, set_trace=False)
# 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')
@ -55,7 +70,7 @@ py_db.ready_to_run = True
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)
@ -65,7 +80,7 @@ py_db.run(%(foo_module)r, is_module=True, set_trace=False)
''' % 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
@ -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)
@ -90,7 +105,7 @@ py_db.ready_to_run = True
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')