Skip to content

Commit 4f2a456

Browse files
committed
Support for Python 3.11: cython. WIP #939
1 parent 161aa26 commit 4f2a456

14 files changed

Lines changed: 2704 additions & 2639 deletions

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_collect_bytecode_info.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ def _lookahead(self):
669669
)
670670
return RESTART_FROM_LOOKAHEAD
671671

672-
if next_instruction.opname == 'CALL_FUNCTION':
672+
if next_instruction.opname in ('CALL_FUNCTION', 'PRECALL'):
673673
if len(found) == next_instruction.argval + 1:
674674
force_restart = False
675675
delta = 0
@@ -680,7 +680,10 @@ def _lookahead(self):
680680
else:
681681
return None # This is odd
682682

683-
del self.instructions[delta:delta + next_instruction.argval + 2] # +2 = NAME / CALL_FUNCTION
683+
del_upto = delta + next_instruction.argval + 2 # +2 = NAME / CALL_FUNCTION
684+
if next_instruction.opname == 'PRECALL':
685+
del_upto += 1 # Also remove the CALL right after the PRECALL.
686+
del self.instructions[delta:del_upto]
684687

685688
found = iter(found[delta:])
686689
call_func = next(found)
@@ -779,8 +782,12 @@ def _create_msg_part(self, instruction, tok=None, line=None):
779782
dec = self._decorate_jump_target
780783
if line is None or line in (self.BIG_LINE_INT, self.SMALL_LINE_INT):
781784
line = self.op_offset_to_line[instruction.offset]
785+
786+
argrepr = instruction.argrepr
787+
if isinstance(argrepr, str) and argrepr.startswith('NULL + '):
788+
argrepr = argrepr[7:]
782789
return _MsgPart(
783-
line, tok if tok is not None else dec(instruction, instruction.argrepr))
790+
line, tok if tok is not None else dec(instruction, argrepr))
784791

785792
def _next_instruction_to_str(self, line_to_contents):
786793
# indent = ''
@@ -797,6 +804,9 @@ def _next_instruction_to_str(self, line_to_contents):
797804

798805
instruction = self.instructions.pop(0)
799806

807+
if instruction.opname in 'RESUME':
808+
return None
809+
800810
if instruction.opname in ('LOAD_GLOBAL', 'LOAD_FAST', 'LOAD_CONST', 'LOAD_NAME'):
801811
next_instruction = self.instructions[0]
802812
if next_instruction.opname in ('STORE_FAST', 'STORE_NAME'):
@@ -855,6 +865,8 @@ def build_line_to_contents(self):
855865
s = self._next_instruction_to_str(line_to_contents)
856866
if s is RESTART_FROM_LOOKAHEAD:
857867
continue
868+
if s is None:
869+
continue
858870

859871
_MsgPart.add_to_line_to_contents(s, line_to_contents)
860872
m = self.max_line(s)

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_cython.c

Lines changed: 2629 additions & 2620 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_cython.pyx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,11 @@ cdef class PyDBFrame:
704704
# if DEBUG: print('frame trace_dispatch %s %s %s %s %s %s, stop: %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, constant_to_str(info.pydev_step_cmd), arg, info.pydev_step_stop))
705705
try:
706706
info.is_tracing += 1
707-
line = frame.f_lineno
707+
708+
# TODO: This shouldn't be needed. The fact that frame.f_lineno
709+
# is None seems like a bug in Python 3.11.
710+
# Reported in: https://github.com/python/cpython/issues/94485
711+
line = frame.f_lineno or 0 # Workaround or case where frame.f_lineno is None
708712
line_cache_key = (frame_cache_key, line)
709713

710714
if main_debugger.pydb_disposed:

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,11 @@ def trace_dispatch(self, frame, event, arg):
571571
# if DEBUG: print('frame trace_dispatch %s %s %s %s %s %s, stop: %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, constant_to_str(info.pydev_step_cmd), arg, info.pydev_step_stop))
572572
try:
573573
info.is_tracing += 1
574-
line = frame.f_lineno
574+
575+
# TODO: This shouldn't be needed. The fact that frame.f_lineno
576+
# is None seems like a bug in Python 3.11.
577+
# Reported in: https://github.com/python/cpython/issues/94485
578+
line = frame.f_lineno or 0 # Workaround or case where frame.f_lineno is None
575579
line_cache_key = (frame_cache_key, line)
576580

577581
if main_debugger.pydb_disposed:

src/debugpy/_vendored/pydevd/_pydevd_frame_eval/pydevd_frame_evaluator.pxd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ cdef extern from "code.h":
5252
ctypedef void freefunc(void *)
5353
int _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
5454
int _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
55+
56+
# TODO: Things are in a different place for Python 3.11.
57+
# cdef extern from "cpython/code.h":
58+
# ctypedef void freefunc(void *)
59+
# int _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
60+
# int _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
5561

5662
cdef extern from "Python.h":
5763
void Py_INCREF(object o)

src/debugpy/_vendored/pydevd/setup_pydevd_cython.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
os.chdir(os.path.dirname(os.path.abspath(__file__)))
1717

1818
IS_PY36_OR_GREATER = sys.version_info > (3, 6)
19+
TODO_PY311 = sys.version_info > (3, 11)
1920

2021

2122
def process_args():
@@ -234,7 +235,7 @@ def build_extension(dir_name, extension_name, target_pydevd_name, force_cython,
234235
target_pydevd_name = extension_name
235236
build_extension("_pydevd_bundle", extension_name, target_pydevd_name, force_cython, extension_folder, True)
236237

237-
if IS_PY36_OR_GREATER:
238+
if IS_PY36_OR_GREATER and not TODO_PY311:
238239
extension_name = "pydevd_frame_evaluator"
239240
if target_frame_eval is None:
240241
target_frame_eval = extension_name

src/debugpy/_vendored/pydevd/tests_python/debug_constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
PYDEVD_TEST_VM = os.getenv('PYDEVD_TEST_VM', None)
77

88
IS_PY36_OR_GREATER = sys.version_info[0:2] >= (3, 6)
9+
IS_PY311_OR_GREATER = sys.version_info[0:2] >= (3, 11)
910
IS_CPYTHON = platform.python_implementation() == 'CPython'
1011

12+
TODO_PY311 = IS_PY311_OR_GREATER # Code which needs to be fixed in 3.11 should use this constant.
13+
1114
IS_PY36 = False
1215
if sys.version_info[0] == 3 and sys.version_info[1] == 6:
1316
IS_PY36 = True

src/debugpy/_vendored/pydevd/tests_python/test_bytecode_manipulation.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55

66
import pytest
77

8-
from tests_python.debug_constants import IS_PY36_OR_GREATER, IS_CPYTHON, TEST_CYTHON
8+
from tests_python.debug_constants import IS_PY36_OR_GREATER, IS_CPYTHON, TEST_CYTHON, TODO_PY311
99

10-
pytestmark = pytest.mark.skipif(not IS_PY36_OR_GREATER or not IS_CPYTHON or not TEST_CYTHON, reason='Requires CPython >= 3.6')
10+
pytestmark = pytest.mark.skipif(
11+
not IS_PY36_OR_GREATER or
12+
TODO_PY311 or
13+
not IS_CPYTHON or
14+
not TEST_CYTHON, reason='Requires CPython >= 3.6 < 3.11')
1115

1216

1317
class _Tracer(object):

src/debugpy/_vendored/pydevd/tests_python/test_collect_bytecode_info.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
collect_return_info, code_to_bytecode_representation
99
from tests_python.debugger_unittest import IS_CPYTHON, IS_PYPY
1010
from _pydevd_bundle.pydevd_constants import IS_PY38_OR_GREATER, IS_JYTHON
11+
from tests_python.debug_constants import IS_PY311_OR_GREATER
1112

1213

1314
def _method_call_with_error():
@@ -192,7 +193,7 @@ def test_collect_try_except_info(data_regression, pyfile):
192193
info = collect_try_except_info(method.__code__, use_func_first_line=True)
193194
method_to_info[key] = sorted(str(x) for x in info)
194195

195-
if sys.version_info[:2] != (3, 10):
196+
if sys.version_info[:2] not in ((3, 10), (3, 11)):
196197
data_regression.check(method_to_info)
197198

198199
data_regression.check(method_to_info_from_source)
@@ -385,6 +386,8 @@ def try_except_with():
385386

386387

387388
def test_collect_try_except_info_async_for():
389+
if IS_PY311_OR_GREATER:
390+
pytest.skip('On Python 3.11 we just support collecting info from the AST.')
388391

389392
# Not valid on Python 2.
390393
code_str = '''
@@ -482,7 +485,8 @@ def method4():
482485
3,
483486
call('tnh %s' % 1))
484487

485-
assert code_to_bytecode_representation(method4.__code__, use_func_first_line=True).count('\n') == 4
488+
contents = code_to_bytecode_representation(method4.__code__, use_func_first_line=True)
489+
assert contents.count('\n') == 4, 'Found:%s' % (contents,)
486490

487491

488492
@pytest.mark.skipif(IS_JYTHON, reason='Jython does not have bytecode support.')
@@ -537,6 +541,7 @@ def method4():
537541
new_repr = code_to_bytecode_representation(method4.__code__, use_func_first_line=True)
538542
assert 'call()' in new_repr
539543
assert 'call(1, 2, 3, a, b, \'x\')' in new_repr
544+
assert 'NULL' not in new_repr
540545

541546

542547
@pytest.mark.skipif(IS_JYTHON, reason='Jython does not have bytecode support.')

src/debugpy/_vendored/pydevd/tests_python/test_debugger.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,8 @@ def test_case_get_next_statement_targets(case_setup):
16891689
# it's also not that bad (that line has no code in the source and
16901690
# executing it will just set the tracing for the method).
16911691
targets.discard(20)
1692+
# On Python 3.11 there's now a line 1 (which should be harmless).
1693+
targets.discard(1)
16921694
expected = set((2, 3, 5, 8, 9, 10, 12, 13, 14, 15, 17, 18, 19, 21))
16931695
assert targets == expected, 'Expected targets to be %s, was: %s' % (expected, targets)
16941696

@@ -3457,7 +3459,7 @@ def test_frame_eval_limitations(case_setup, filename, break_at_lines):
34573459
hit = writer.wait_for_breakpoint_hit()
34583460
thread_id = hit.thread_id
34593461

3460-
if IS_PY36_OR_GREATER and TEST_CYTHON:
3462+
if (IS_PY36_OR_GREATER and TEST_CYTHON) and not TODO_PY311:
34613463
assert hit.suspend_type == break_mode
34623464
else:
34633465
# Before 3.6 frame eval is not available.
@@ -3496,6 +3498,7 @@ def test_step_return_my_code(case_setup):
34963498
writer.finished_ok = True
34973499

34983500

3501+
@pytest.mark.skipif(TODO_PY311, reason='Needs bytecode support in Python 3.11')
34993502
def test_smart_step_into_case1(case_setup):
35003503
with case_setup.test_file('_debugger_case_smart_step_into.py') as writer:
35013504
line = writer.get_line_index_with_content('break here')
@@ -3518,6 +3521,7 @@ def test_smart_step_into_case1(case_setup):
35183521
writer.finished_ok = True
35193522

35203523

3524+
@pytest.mark.skipif(TODO_PY311, reason='Needs bytecode support in Python 3.11')
35213525
def test_smart_step_into_case2(case_setup):
35223526
with case_setup.test_file('_debugger_case_smart_step_into2.py') as writer:
35233527
line = writer.get_line_index_with_content('break here')
@@ -3546,6 +3550,7 @@ def test_smart_step_into_case2(case_setup):
35463550
writer.finished_ok = True
35473551

35483552

3553+
@pytest.mark.skipif(TODO_PY311, reason='Needs bytecode support in Python 3.11')
35493554
def test_smart_step_into_case3(case_setup):
35503555
with case_setup.test_file('_debugger_case_smart_step_into3.py') as writer:
35513556
line = writer.get_line_index_with_content('break here')

0 commit comments

Comments
 (0)