Get Python 3.13 to work (#1692)

* Get debug launch working

* Turn Cython support back on

* Fix test failures

* Fix gevent test to be skipped

* Missed a version change in pipeline

* Fix comment

* Review feedback
This commit is contained in:
Rich Chiodo 2024-10-10 09:03:34 -07:00 committed by GitHub
parent 25955a05d8
commit f7d5df027c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 7133 additions and 6877 deletions

View file

@ -65,6 +65,8 @@ jobs:
python.version: "3.11"
py312:
python.version: "3.12"
py313:
python.version: "3.13"
steps:
@ -93,6 +95,8 @@ jobs:
python.version: "3.11"
py312:
python.version: "3.12"
py313:
python.version: "3.13"
steps:
@ -124,6 +128,8 @@ jobs:
python.version: "3.11"
py312:
python.version: "3.12"
py313:
python.version: "3.13"
steps:

View file

@ -4,7 +4,7 @@ from _pydev_bundle._pydev_saved_modules import threading
# circumstances).
# It is required to debug threads started by start_new_thread in Python 3.4
_temp = threading.Thread()
if hasattr(_temp, "_is_stopped"): # Python 3.x has this
if hasattr(_temp, "_is_stopped"): # Python 3.12 and earlier has this
def is_thread_alive(t):
return not t._is_stopped

View file

@ -10,6 +10,7 @@ from _pydevd_bundle.pydevd_constants import (
)
from _pydev_bundle import pydev_log
from _pydev_bundle._pydev_saved_modules import threading
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
import weakref
version = 11
@ -135,7 +136,7 @@ class PyDBAdditionalThreadInfo(object):
if thread is None:
return False
if thread._is_stopped:
if not is_thread_alive(thread):
return None
if thread._ident is None: # Can this happen?

View file

@ -173,9 +173,11 @@ IS_PY39_OR_GREATER = sys.version_info >= (3, 9)
IS_PY310_OR_GREATER = sys.version_info >= (3, 10)
IS_PY311_OR_GREATER = sys.version_info >= (3, 11)
IS_PY312_OR_GREATER = sys.version_info >= (3, 12)
IS_PY313_OR_GREATER = sys.version_info >= (3, 13)
IS_PY314_OR_GREATER = sys.version_info >= (3, 14)
# Not currently supported in Python 3.12.
SUPPORT_ATTACH_TO_PID = not IS_PY312_OR_GREATER
# Not currently supported in Python 3.14.
SUPPORT_ATTACH_TO_PID = not IS_PY314_OR_GREATER
def version_str(v):

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@ from _pydevd_bundle.pydevd_constants import (
)
from _pydev_bundle import pydev_log
from _pydev_bundle._pydev_saved_modules import threading
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
import weakref
version = 11
@ -141,7 +142,7 @@ cdef class PyDBAdditionalThreadInfo:
if thread is None:
return False
if thread._is_stopped:
if not is_thread_alive(thread):
return None
if thread._ident is None: # Can this happen?

View file

@ -35,7 +35,12 @@ def add_exception_to_frame(frame, exception_info):
def remove_exception_from_frame(frame):
frame.f_locals.pop("__exception__", None)
# In 3.13 frame.f_locals became a proxy for a dict, so we need to copy it to a real dict
# so we can call the defined update method. Just deleting the entry throws in 3.13.
items = {key: value for key, value in frame.f_locals.items()}
if "__exception__" in items:
del items["__exception__"]
frame.f_locals.update(items)
FILES_WITH_IMPORT_HOOKS = ["pydev_monkey_qt.py", "pydev_import_hook.py"]
@ -140,6 +145,7 @@ _utf8_with_4_bytes = 0x10000
def _utf8_byte_offset_to_character_offset(s: str, offset: int):
byte_offset = 0
char_offset = 0
offset = offset or 0
for char_offset, character in enumerate(s):
byte_offset += 1

View file

@ -13,6 +13,7 @@ from typing import Dict, Optional, Tuple, Any
from os.path import basename, splitext
from _pydev_bundle import pydev_log
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydevd_bundle import pydevd_dont_trace
from _pydevd_bundle.pydevd_constants import (
GlobalDebuggerHolder,
@ -459,9 +460,10 @@ def _get_code_line_info(code_obj, _cache={}):
last_line = None
for offset, line in dis.findlinestarts(code_obj):
line_to_offset[line] = offset
if offset is not None and line is not None:
line_to_offset[line] = offset
if line_to_offset:
if len(line_to_offset):
first_line = min(line_to_offset)
last_line = max(line_to_offset)
ret = _CodeLineInfo(line_to_offset, first_line, last_line)
@ -837,7 +839,7 @@ def _unwind_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -911,7 +913,7 @@ def _raise_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1029,7 +1031,7 @@ def _return_event(code, instruction, retval):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1340,7 +1342,7 @@ def _jump_event(code, from_offset, to_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1397,7 +1399,7 @@ def _line_event(code, line):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1644,7 +1646,7 @@ def _start_method_event(code, instruction_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return

View file

@ -13,13 +13,13 @@ import dis
import os
import re
import sys
from _pydev_bundle._pydev_saved_modules import threading
from types import CodeType, FrameType
from typing import Dict, Optional, Tuple, Any
from os.path import basename, splitext
from _pydev_bundle import pydev_log
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydevd_bundle import pydevd_dont_trace
from _pydevd_bundle.pydevd_constants import (
GlobalDebuggerHolder,
@ -466,9 +466,10 @@ cdef _get_code_line_info(code_obj, _cache={}):
last_line = None
for offset, line in dis.findlinestarts(code_obj):
line_to_offset[line] = offset
if offset is not None and line is not None:
line_to_offset[line] = offset
if line_to_offset:
if len(line_to_offset):
first_line = min(line_to_offset)
last_line = max(line_to_offset)
ret = _CodeLineInfo(line_to_offset, first_line, last_line)
@ -844,7 +845,7 @@ cdef _unwind_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -918,7 +919,7 @@ cdef _raise_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1036,7 +1037,7 @@ cdef _return_event(code, instruction, retval):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1347,7 +1348,7 @@ cdef _jump_event(code, int from_offset, int to_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1404,7 +1405,7 @@ cdef _line_event(code, int line):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1651,7 +1652,7 @@ cdef _start_method_event(code, instruction_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or thread_info.thread._is_stopped:
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return

View file

@ -7,6 +7,7 @@ PYDEVD_TEST_VM = os.getenv("PYDEVD_TEST_VM", None)
IS_PY36_OR_GREATER = sys.version_info[0:2] >= (3, 6)
IS_PY311_OR_GREATER = sys.version_info[0:2] >= (3, 11)
IS_PY313_OR_GREATER = sys.version_info[0:2] >= (3, 13)
IS_PY311 = sys.version_info[0:2] == (3, 11)
IS_PY312 = sys.version_info[0:2] == (3, 12)
IS_CPYTHON = platform.python_implementation() == "CPython"

View file

@ -353,9 +353,9 @@ def test_exception_stack(pyfile, target, run, max_frames):
session.expected_exit_code = some.int
max_frames, (min_expected_lines, max_expected_lines) = {
"all": (0, (100, 221)),
"default": (None, (100, 221)),
10: (10, (10, 22)),
"all": (0, (100, 308)),
"default": (None, (100, 308)),
10: (10, (10, 32)),
}[max_frames]
if max_frames is not None:
session.config["maxExceptionStackFrames"] = max_frames

View file

@ -2,10 +2,12 @@
# Licensed under the MIT License. See LICENSE in the project root
# for license information.
import pytest
from tests import debug
from tests.patterns import some
from _pydevd_bundle.pydevd_constants import IS_PY313_OR_GREATER
@pytest.mark.skipif(IS_PY313_OR_GREATER, reason="gevent is not up to date with 3.13 (_tstate_lock is not part of thread anymore)")
def test_gevent(pyfile, target, run):
@pyfile
def code_to_debug():

View file

@ -1,5 +1,5 @@
[tox]
envlist = py{38,39,310,311,312}{,-cov}
envlist = py{38,39,310,311,312,313}{,-cov}
[testenv]
deps = -rtests/requirements.txt
@ -9,5 +9,5 @@ setenv =
commands =
py{38,39}-!cov: python -m pytest {posargs}
py{38,39}-cov: python -m pytest --cov --cov-append --cov-config=.coveragerc {posargs}
py{310,311,312}-!cov: python -Xfrozen_modules=off -m pytest {posargs}
py{310,311,312}-cov: python -Xfrozen_modules=off -m pytest --cov --cov-append --cov-config=.coveragerc {posargs}
py{310,311,312,313}-!cov: python -Xfrozen_modules=off -m pytest {posargs}
py{310,311,312,313}-cov: python -Xfrozen_modules=off -m pytest --cov --cov-append --cov-config=.coveragerc {posargs}