Skip to content

Commit

Permalink
Update pydevd
Browse files Browse the repository at this point in the history
fabioz committed Oct 27, 2024
1 parent 17618c3 commit 50fefb1
Showing 49 changed files with 8,726 additions and 8,062 deletions.
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v3
- name: Set up Python
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@ jobs:
"ubuntu-py311-cython",
"ubuntu-py312-cython-checkbin",
"windows-py312-cython-checkbin",
"ubuntu-py313-cython",
"windows-py313-cython",
]

include:
@@ -64,6 +66,14 @@ jobs:
python: "3.12"
os: windows-latest
PYDEVD_USE_CYTHON: YES
- name: "ubuntu-py313-cython"
python: "3.13"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: YES
- name: "windows-py313-cython"
python: "3.13"
os: windows-latest
PYDEVD_USE_CYTHON: YES

steps:
- uses: actions/checkout@v1
@@ -95,7 +105,7 @@ jobs:
pip install untangle --no-warn-script-location
pip install importlib-metadata --no-warn-script-location
- name: Install Python 3.x deps
if: contains(matrix.name, 'py3') && !contains(matrix.name, 'pypy') && !contains(matrix.name, 'py312') && !contains(matrix.name, 'py311')
if: contains(matrix.name, 'py3') && !contains(matrix.name, 'pypy') && !contains(matrix.name, 'py312') && !contains(matrix.name, 'py311') && !contains(matrix.name, 'py313')
run: |
pip install PySide2 --no-warn-script-location
pip install "numpy<2" --force --no-warn-script-location
@@ -118,13 +128,14 @@ jobs:
- name: Check that wheels can be built
if: contains(matrix.name, 'checkbin') && contains(matrix.name, 'ubuntu')
run: |
python -m pip install cibuildwheel==2.21.2
python -m pip install setuptools --no-warn-script-location
python -m pip install cibuildwheel==2.21.3
# Remove these .so files (will be rebuilt)
rm pydevd_attach_to_process/*.so
python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_BUILD: cp310-*manylinux*x86_64 cp311-*manylinux*x86_64 cp312-*manylinux*x86_64
CIBW_BUILD_VERBOSITY: 1
CIBW_BUILD: cp310-*manylinux*x86_64 cp311-*manylinux*x86_64 cp312-*manylinux*x86_64 cp313-*manylinux*x86_64
CIBW_BUILD_VERBOSITY: 3

- name: Rebuild .so
if: contains(matrix.name, 'checkbin') && contains(matrix.name, 'ubuntu')
Original file line number Diff line number Diff line change
@@ -4,7 +4,14 @@
# 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, "_handle") and hasattr(_temp, "_started"): # Python 3.13 and later has this

def is_thread_alive(t):
return not t._handle.is_done()


elif hasattr(_temp, "_is_stopped"): # Python 3.12 and earlier has this

def is_thread_alive(t):
return not t._is_stopped
82 changes: 58 additions & 24 deletions plugins/org.python.pydev.core/pysrc/_pydev_bundle/pydev_monkey.py
Original file line number Diff line number Diff line change
@@ -12,17 +12,14 @@
set_global_debugger,
DebugInfoHolder,
PYDEVD_USE_SYS_MONITORING,
IS_PY313_OR_GREATER,
)
from _pydev_bundle import pydev_log
from contextlib import contextmanager
from _pydevd_bundle import pydevd_constants, pydevd_defaults
from _pydevd_bundle.pydevd_defaults import PydevdCustomization
import ast

try:
from pathlib import Path
except ImportError:
Path = None
from pathlib import Path

# ===============================================================================
# Things that are dependent on having the pydevd debugger
@@ -299,7 +296,7 @@ def remove_quotes_from_args(args):
new_args = []

for x in args:
if Path is not None and isinstance(x, Path):
if isinstance(x, Path):
x = str(x)
else:
if not isinstance(x, (bytes, str)):
@@ -316,7 +313,7 @@ def remove_quotes_from_args(args):
else:
new_args = []
for x in args:
if Path is not None and isinstance(x, Path):
if isinstance(x, Path):
x = x.as_posix()
else:
if not isinstance(x, (bytes, str)):
@@ -1173,15 +1170,31 @@ def _get_threading_modules_to_patch():


def patch_thread_module(thread_module):
if getattr(thread_module, "_original_start_new_thread", None) is None:
if thread_module is threading:
if not hasattr(thread_module, "_start_new_thread"):
return # Jython doesn't have it.
_original_start_new_thread = thread_module._original_start_new_thread = thread_module._start_new_thread
# Note: this is needed not just for the tracing, but to have an early way to
# notify that a thread was created (i.e.: tests_python.test_debugger_json.test_case_started_exited_threads_protocol)
start_thread_attrs = ["_start_new_thread", "start_new_thread", "start_new"]
start_joinable_attrs = ["start_joinable_thread", "_start_joinable_thread"]
check = start_thread_attrs + start_joinable_attrs

replace_attrs = []
for attr in check:
if hasattr(thread_module, attr):
replace_attrs.append(attr)

if not replace_attrs:
return

for attr in replace_attrs:
if attr in start_joinable_attrs:
if getattr(thread_module, "_original_start_joinable_thread", None) is None:
_original_start_joinable_thread = thread_module._original_start_joinable_thread = getattr(thread_module, attr)
else:
_original_start_joinable_thread = thread_module._original_start_joinable_thread
else:
_original_start_new_thread = thread_module._original_start_new_thread = thread_module.start_new_thread
else:
_original_start_new_thread = thread_module._original_start_new_thread
if getattr(thread_module, "_original_start_new_thread", None) is None:
_original_start_new_thread = thread_module._original_start_new_thread = getattr(thread_module, attr)
else:
_original_start_new_thread = thread_module._original_start_new_thread

class ClassWithPydevStartNewThread:
def pydev_start_new_thread(self, function, args=(), kwargs={}):
@@ -1191,6 +1204,19 @@ def pydev_start_new_thread(self, function, args=(), kwargs={}):
"""
return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ())

class ClassWithPydevStartJoinableThread:
def pydev_start_joinable_thread(self, function, *args, **kwargs):
"""
We need to replace the original thread_module._start_joinable_thread with this function so that threads started
through it and not through the threading module are properly traced.
"""
# Note: only handling the case from threading.py where the handle
# and daemon flags are passed explicitly. This will fail if some user library
# actually passes those without being a keyword argument!
handle = kwargs.pop("handle", None)
daemon = kwargs.pop("daemon", True)
return _original_start_joinable_thread(_UseNewThreadStartup(function, args, kwargs), handle=handle, daemon=daemon)

# This is a hack for the situation where the thread_module.start_new_thread is declared inside a class, such as the one below
# class F(object):
# start_new_thread = thread_module.start_new_thread
@@ -1200,17 +1226,15 @@ def pydev_start_new_thread(self, function, args=(), kwargs={}):
# So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it
# does work in the default case because in builtins self isn't passed either.
pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread
pydev_start_joinable_thread = ClassWithPydevStartJoinableThread().pydev_start_joinable_thread

try:
# We need to replace the original thread_module.start_new_thread with this function so that threads started through
# it and not through the threading module are properly traced.
if thread_module is threading:
thread_module._start_new_thread = pydev_start_new_thread
# We need to replace the original thread_module.start_new_thread with this function so that threads started through
# it and not through the threading module are properly traced.
for attr in replace_attrs:
if attr in start_joinable_attrs:
setattr(thread_module, attr, pydev_start_joinable_thread)
else:
thread_module.start_new_thread = pydev_start_new_thread
thread_module.start_new = pydev_start_new_thread
except:
pass
setattr(thread_module, attr, pydev_start_new_thread)


def patch_thread_modules():
@@ -1235,6 +1259,16 @@ def undo_patch_thread_modules():
except:
pass

try:
t._start_joinable_thread = t._original_start_joinable_thread
except:
pass

try:
t.start_joinable_thread = t._original_start_joinable_thread
except:
pass


def disable_trace_thread_modules():
"""
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
)
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 @@ def _get_related_thread(self):
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?
Original file line number Diff line number Diff line change
@@ -846,6 +846,8 @@ def _create_msg_part(self, instruction, tok=None, line=None):
argrepr = instruction.argrepr
if isinstance(argrepr, str) and argrepr.startswith("NULL + "):
argrepr = argrepr[7:]
if isinstance(argrepr, str) and argrepr.endswith("+ NULL"):
argrepr = argrepr[:-7]
return _MsgPart(line, tok if tok is not None else dec(instruction, argrepr))

def _next_instruction_to_str(self, line_to_contents):
23 changes: 12 additions & 11 deletions plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
@@ -1140,7 +1140,8 @@ def internal_get_next_statement_targets(dbg, seq, thread_id, frame_id):
xml += "<line>%d</line>" % (frame.f_lineno,)
else:
for _, line in linestarts:
xml += "<line>%d</line>" % (line,)
if line is not None:
xml += "<line>%d</line>" % (line,)
del frame
xml += "</xml>"
cmd = dbg.cmd_factory.make_get_next_statement_targets_message(seq, xml)
@@ -1342,9 +1343,10 @@ def internal_evaluate_expression(dbg, seq, thread_id, frame_id, expression, is_e
dbg.writer.add_command(cmd)


def _set_expression_response(py_db, request, result, error_message):
body = pydevd_schema.SetExpressionResponseBody(result="", variablesReference=0)
variables_response = pydevd_base_schema.build_response(request, kwargs={"body": body, "success": False, "message": error_message})
def _set_expression_response(py_db, request, error_message):
body = pydevd_schema.SetExpressionResponseBody(value='')
variables_response = pydevd_base_schema.build_response(request, kwargs={
'body':body, 'success':False, 'message': error_message})
py_db.writer.add_command(NetCommand(CMD_RETURN, 0, variables_response, is_json=True))


@@ -1360,19 +1362,18 @@ def internal_set_expression_json(py_db, request, thread_id):
fmt = fmt.to_dict()

frame = py_db.find_frame(thread_id, frame_id)
exec_code = "%s = (%s)" % (expression, value)
result = pydevd_vars.evaluate_expression(py_db, frame, exec_code, is_exec=True)
is_error = isinstance(result, ExceptionOnEvaluate)

if is_error:
_set_expression_response(py_db, request, result, error_message="Error executing: %s" % (exec_code,))
exec_code = '%s = (%s)' % (expression, value)
try:
pydevd_vars.evaluate_expression(py_db, frame, exec_code, is_exec=True)
except (Exception, KeyboardInterrupt):
_set_expression_response(py_db, request, error_message='Error executing: %s' % (exec_code,))
return

# Ok, we have the result (could be an error), let's put it into the saved variables.
frame_tracker = py_db.suspended_frames_manager.get_frame_tracker(thread_id)
if frame_tracker is None:
# This is not really expected.
_set_expression_response(py_db, request, result, error_message="Thread id: %s is not current thread id." % (thread_id,))
_set_expression_response(py_db, request, error_message='Thread id: %s is not current thread id.' % (thread_id,))
return

# Now that the exec is done, get the actual value changed to return.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
This module holds the constants used for specifying the states of the debugger.
"""

from __future__ import nested_scopes
import platform
import weakref
@@ -174,9 +175,17 @@ def _current_frames():
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)

# Bug affecting Python 3.13.0 specifically makes some tests crash the interpreter!
# Hopefully it'll be fixed in 3.13.1.
IS_PY313_0 = sys.version_info[:3] == (3, 13, 0)

# Mark tests that need to be fixed with this.
TODO_PY313_OR_GREATER = IS_PY313_OR_GREATER

# Not currently supported in Python 3.12.
SUPPORT_ATTACH_TO_PID = not IS_PY313_OR_GREATER
# Not currently supported in Python 3.14.
SUPPORT_ATTACH_TO_PID = not IS_PY314_OR_GREATER


def version_str(v):
Loading

0 comments on commit 50fefb1

Please sign in to comment.