Assorted fixes for Python 3.10 support:

Split test/requirements.txt into two different lists, one for py2, and the other for py3; update package versions in py3 list as needed to support py3.10.

Fix usage of deprecated threading functions getName(), setName(), isDaemon(), and currentThread().

Refactor test_invalid_breakpoints to be more declarative to handle Python version differences more easily and clearly, and fix it for py3.10.

Disable Django tests on py3.10 (#689).

Disable gevent tests on py3.10 (#688).
This commit is contained in:
Pavel Minaev 2021-08-04 18:09:14 -07:00 committed by Fabio Zadrozny
parent 0e5b8f7a7c
commit 3a9f7d64c4
23 changed files with 131 additions and 79 deletions

View file

@ -559,7 +559,7 @@ class BaseInterpreterInterface:
from _pydevd_bundle.pydevd_constants import set_thread_id from _pydevd_bundle.pydevd_constants import set_thread_id
from _pydev_bundle import pydev_localhost from _pydev_bundle import pydev_localhost
set_thread_id(threading.currentThread(), "console_main") set_thread_id(threading.current_thread(), "console_main")
VIRTUAL_FRAME_ID = "1" # matches PyStackFrameConsole.java VIRTUAL_FRAME_ID = "1" # matches PyStackFrameConsole.java
VIRTUAL_CONSOLE_ID = "console_main" # matches PyThreadConsole.java VIRTUAL_CONSOLE_ID = "console_main" # matches PyThreadConsole.java

View file

@ -1032,12 +1032,12 @@ class _NewThreadStartupWithTrace:
# Note: if this is a thread from threading.py, we're too early in the boostrap process (because we mocked # Note: if this is a thread from threading.py, we're too early in the boostrap process (because we mocked
# the start_new_thread internal machinery and thread._bootstrap has not finished), so, the code below needs # the start_new_thread internal machinery and thread._bootstrap has not finished), so, the code below needs
# to make sure that we use the current thread bound to the original function and not use # to make sure that we use the current thread bound to the original function and not use
# threading.currentThread() unless we're sure it's a dummy thread. # current_thread() unless we're sure it's a dummy thread.
t = getattr(self.original_func, '__self__', getattr(self.original_func, 'im_self', None)) t = getattr(self.original_func, '__self__', getattr(self.original_func, 'im_self', None))
if not isinstance(t, threading.Thread): if not isinstance(t, threading.Thread):
# This is not a threading.Thread but a Dummy thread (so, get it as a dummy thread using # This is not a threading.Thread but a Dummy thread (so, get it as a dummy thread using
# currentThread). # currentThread).
t = threading.currentThread() t = threading.current_thread()
if not getattr(t, 'is_pydev_daemon_thread', False): if not getattr(t, 'is_pydev_daemon_thread', False):
thread_id = get_current_thread_id(t) thread_id = get_current_thread_id(t)

View file

@ -152,7 +152,7 @@ class ReaderThread(PyDBDaemonThread):
self.sock = sock self.sock = sock
self._buffer = b'' self._buffer = b''
self.setName("pydevd.Reader") self.name = "pydevd.Reader"
self.process_net_command = process_net_command self.process_net_command = process_net_command
self.process_net_command_json = PyDevJsonCommandProcessor(self._from_json).process_net_command_json self.process_net_command_json = PyDevJsonCommandProcessor(self._from_json).process_net_command_json
@ -323,7 +323,7 @@ class FSNotifyThread(PyDBDaemonThread):
def __init__(self, py_db, api, watch_dirs): def __init__(self, py_db, api, watch_dirs):
PyDBDaemonThread.__init__(self, py_db) PyDBDaemonThread.__init__(self, py_db)
self.api = api self.api = api
self.setName("pydevd.FSNotifyThread") self.name = "pydevd.FSNotifyThread"
self.watcher = fsnotify.Watcher() self.watcher = fsnotify.Watcher()
self.watch_dirs = watch_dirs self.watch_dirs = watch_dirs
@ -359,7 +359,7 @@ class WriterThread(PyDBDaemonThread):
PyDBDaemonThread.__init__(self, py_db) PyDBDaemonThread.__init__(self, py_db)
self.sock = sock self.sock = sock
self.__terminate_on_socket_close = terminate_on_socket_close self.__terminate_on_socket_close = terminate_on_socket_close
self.setName("pydevd.Writer") self.name = "pydevd.Writer"
self._cmd_queue = _queue.Queue() self._cmd_queue = _queue.Queue()
if pydevd_vm_type.get_vm_type() == 'python': if pydevd_vm_type.get_vm_type() == 'python':
self.timeout = 0 self.timeout = 0

View file

@ -75,7 +75,7 @@ def add_custom_frame(frame, name, thread_id):
Returns the custom thread id which will be used to show the given frame paused. Returns the custom thread id which will be used to show the given frame paused.
''' '''
with CustomFramesContainer.custom_frames_lock: with CustomFramesContainer.custom_frames_lock:
curr_thread_id = get_current_thread_id(threading.currentThread()) curr_thread_id = get_current_thread_id(threading.current_thread())
next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1 next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1
# Note: the frame id kept contains an id and thread information on the thread where the frame was added # Note: the frame id kept contains an id and thread information on the thread where the frame was added

View file

@ -35,7 +35,7 @@ class PyDBDaemonThread(threading.Thread):
created_pydb_daemon[self] = 1 created_pydb_daemon[self] = 1
try: try:
try: try:
if IS_JYTHON and not isinstance(threading.currentThread(), threading._MainThread): if IS_JYTHON and not isinstance(threading.current_thread(), threading._MainThread):
# we shouldn't update sys.modules for the main thread, cause it leads to the second importing 'threading' # we shouldn't update sys.modules for the main thread, cause it leads to the second importing 'threading'
# module, and the new instance of main thread is created # module, and the new instance of main thread is created
ss = JyCore.PySystemState() ss = JyCore.PySystemState()
@ -59,7 +59,7 @@ class PyDBDaemonThread(threading.Thread):
def do_kill_pydev_thread(self): def do_kill_pydev_thread(self):
if not self._kill_received: if not self._kill_received:
pydev_log.debug('%s received kill signal', self.getName()) pydev_log.debug('%s received kill signal', self.name)
self._kill_received = True self._kill_received = True
def _stop_trace(self): def _stop_trace(self):

View file

@ -142,7 +142,7 @@ class NetCommandFactoryJson(NetCommandFactory):
# Notify that it's created (no-op if we already notified before). # Notify that it's created (no-op if we already notified before).
py_db.notify_thread_created(thread_id, thread) py_db.notify_thread_created(thread_id, thread)
thread_schema = pydevd_schema.Thread(id=thread_id, name=thread.getName()) thread_schema = pydevd_schema.Thread(id=thread_id, name=thread.name)
threads.append(thread_schema.to_dict()) threads.append(thread_schema.to_dict())
body = pydevd_schema.ThreadsResponseBody(threads) body = pydevd_schema.ThreadsResponseBody(threads)

View file

@ -47,7 +47,7 @@ class NetCommandFactory(object):
def _thread_to_xml(self, thread): def _thread_to_xml(self, thread):
""" thread information as XML """ """ thread information as XML """
name = pydevd_xml.make_valid_xml_value(thread.getName()) name = pydevd_xml.make_valid_xml_value(thread.name)
cmdText = '<thread name="%s" id="%s" />' % (quote(name), get_thread_id(thread)) cmdText = '<thread name="%s" id="%s" />' % (quote(name), get_thread_id(thread))
return cmdText return cmdText

View file

@ -448,4 +448,3 @@ def interrupt_main_thread(main_thread):
main_thread._thread.interrupt() # Jython main_thread._thread.interrupt() # Jython
except: except:
pydev_log.exception('Error on interrupt main thread fallback.') pydev_log.exception('Error on interrupt main thread fallback.')

View file

@ -41,7 +41,7 @@ def iter_frames(frame):
def dump_frames(thread_id): def dump_frames(thread_id):
sys.stdout.write('dumping frames\n') sys.stdout.write('dumping frames\n')
if thread_id != get_current_thread_id(threading.currentThread()): if thread_id != get_current_thread_id(threading.current_thread()):
raise VariableError("find_frame: must execute on same thread") raise VariableError("find_frame: must execute on same thread")
frame = get_frame() frame = get_frame()
@ -65,7 +65,7 @@ def getVariable(dbg, thread_id, frame_id, scope, attrs):
not the frame (as we don't care about the frame in this case). not the frame (as we don't care about the frame in this case).
""" """
if scope == 'BY_ID': if scope == 'BY_ID':
if thread_id != get_current_thread_id(threading.currentThread()): if thread_id != get_current_thread_id(current_thread()):
raise VariableError("getVariable: must execute on same thread") raise VariableError("getVariable: must execute on same thread")
try: try:

View file

@ -34,7 +34,7 @@ def _get_line_for_frame(frame):
def _pydev_stop_at_break(line): def _pydev_stop_at_break(line):
frame = sys._getframe(1) frame = sys._getframe(1)
# print('pydevd SET TRACING at ', line, 'curr line', frame.f_lineno) # print('pydevd SET TRACING at ', line, 'curr line', frame.f_lineno)
t = threading.currentThread() t = threading.current_thread()
try: try:
additional_info = t.additional_info additional_info = t.additional_info
except: except:
@ -73,7 +73,7 @@ def _pydev_needs_stop_at_break(line):
# then, proceed to go to the current line # then, proceed to go to the current line
# (which will then trigger a line event). # (which will then trigger a line event).
''' '''
t = threading.currentThread() t = threading.current_thread()
try: try:
additional_info = t.additional_info additional_info = t.additional_info
except: except:

View file

@ -126,7 +126,7 @@ if __name__ == '__main__':
sys.exit = skip_successful_exit sys.exit = skip_successful_exit
connect_status_queue = _queue.Queue() connect_status_queue = _queue.Queue()
interpreter = InterpreterInterface(host, int(client_port), threading.currentThread(), connect_status_queue=connect_status_queue) interpreter = InterpreterInterface(host, int(client_port), threading.current_thread(), connect_status_queue=connect_status_queue)
server_thread = threading.Thread(target=start_console_server, server_thread = threading.Thread(target=start_console_server,
name='ServerThread', name='ServerThread',

View file

@ -440,7 +440,7 @@ def start_server(host, port, client_port):
# note that this does not work in jython!!! (sys method can't be replaced). # note that this does not work in jython!!! (sys method can't be replaced).
sys.exit = do_exit sys.exit = do_exit
interpreter = InterpreterInterface(host, client_port, threading.currentThread()) interpreter = InterpreterInterface(host, client_port, threading.current_thread())
start_new_thread(start_console_server, (host, port, interpreter)) start_new_thread(start_console_server, (host, port, interpreter))
@ -457,7 +457,7 @@ def get_interpreter():
try: try:
interpreterInterface = getattr(__builtin__, 'interpreter') interpreterInterface = getattr(__builtin__, 'interpreter')
except AttributeError: except AttributeError:
interpreterInterface = InterpreterInterface(None, None, threading.currentThread()) interpreterInterface = InterpreterInterface(None, None, threading.current_thread())
__builtin__.interpreter = interpreterInterface __builtin__.interpreter = interpreterInterface
sys.stderr.write(interpreterInterface.get_greeting_msg()) sys.stderr.write(interpreterInterface.get_greeting_msg())
sys.stderr.flush() sys.stderr.flush()

View file

@ -153,7 +153,7 @@ if SUPPORT_PLUGINS:
from _pydevd_bundle.pydevd_plugin_utils import PluginManager from _pydevd_bundle.pydevd_plugin_utils import PluginManager
threadingEnumerate = threading.enumerate threadingEnumerate = threading.enumerate
threadingCurrentThread = threading.currentThread threadingCurrentThread = threading.current_thread
try: try:
'dummy'.encode('utf-8') # Added because otherwise Jython 2.2.1 wasn't finding the encoding (if it wasn't loaded in the main thread). 'dummy'.encode('utf-8') # Added because otherwise Jython 2.2.1 wasn't finding the encoding (if it wasn't loaded in the main thread).
@ -179,7 +179,7 @@ class PyDBCommandThread(PyDBDaemonThread):
def __init__(self, py_db): def __init__(self, py_db):
PyDBDaemonThread.__init__(self, py_db) PyDBDaemonThread.__init__(self, py_db)
self._py_db_command_thread_event = py_db._py_db_command_thread_event self._py_db_command_thread_event = py_db._py_db_command_thread_event
self.setName('pydevd.CommandThread') self.name = 'pydevd.CommandThread'
@overrides(PyDBDaemonThread._on_run) @overrides(PyDBDaemonThread._on_run)
def _on_run(self): def _on_run(self):
@ -223,7 +223,7 @@ class CheckAliveThread(PyDBDaemonThread):
def __init__(self, py_db): def __init__(self, py_db):
PyDBDaemonThread.__init__(self, py_db) PyDBDaemonThread.__init__(self, py_db)
self.setName('pydevd.CheckAliveThread') self.name = 'pydevd.CheckAliveThread'
self.daemon = False self.daemon = False
self._wait_event = threading.Event() self._wait_event = threading.Event()
@ -1317,7 +1317,7 @@ class PyDB(object):
'Error in debugger: Found PyDBDaemonThread not marked with is_pydev_daemon_thread=True.\n') 'Error in debugger: Found PyDBDaemonThread not marked with is_pydev_daemon_thread=True.\n')
if is_thread_alive(t): if is_thread_alive(t):
if not t.isDaemon() or hasattr(t, "__pydevd_main_thread"): if not t.daemon or hasattr(t, "__pydevd_main_thread"):
return True return True
return False return False
@ -2159,8 +2159,8 @@ class PyDB(object):
break break
time.sleep(1 / 10.) time.sleep(1 / 10.)
else: else:
thread_names = [t.getName() for t in get_pydb_daemon_threads_to_wait()] thread_names = [t.name for t in get_pydb_daemon_threads_to_wait()]
if thread_names: if thread_names:
pydev_log.debug("The following pydb threads may not have finished correctly: %s", pydev_log.debug("The following pydb threads may not have finished correctly: %s",
', '.join(thread_names)) ', '.join(thread_names))
finally: finally:
@ -2345,8 +2345,8 @@ class PyDB(object):
if self.thread_analyser is not None: if self.thread_analyser is not None:
wrap_threads() wrap_threads()
self.thread_analyser.set_start_time(cur_time()) self.thread_analyser.set_start_time(cur_time())
send_concurrency_message("threading_event", 0, t.getName(), thread_id, "thread", "start", file, 1, None, parent=thread_id) send_concurrency_message("threading_event", 0, t.name, thread_id, "thread", "start", file, 1, None, parent=thread_id)
if self.asyncio_analyser is not None: if self.asyncio_analyser is not None:
# we don't have main thread in asyncio graph, so we should add a fake event # we don't have main thread in asyncio graph, so we should add a fake event
send_concurrency_message("asyncio_event", 0, "Task", "Task", "thread", "stop", file, 1, frame=None, parent=None) send_concurrency_message("asyncio_event", 0, "Task", "Task", "thread", "stop", file, 1, frame=None, parent=None)
@ -2400,7 +2400,7 @@ class PyDB(object):
def wait_for_commands(self, globals): def wait_for_commands(self, globals):
self._activate_mpl_if_needed() self._activate_mpl_if_needed()
thread = threading.currentThread() thread = threading.current_thread()
from _pydevd_bundle import pydevd_frame_utils from _pydevd_bundle import pydevd_frame_utils
frame = pydevd_frame_utils.Frame(None, -1, pydevd_frame_utils.FCode("Console", frame = pydevd_frame_utils.Frame(None, -1, pydevd_frame_utils.FCode("Console",
os.path.abspath(os.path.dirname(__file__))), globals, globals) os.path.abspath(os.path.dirname(__file__))), globals, globals)
@ -2926,7 +2926,7 @@ class DispatchReader(ReaderThread):
@overrides(ReaderThread._on_run) @overrides(ReaderThread._on_run)
def _on_run(self): def _on_run(self):
dummy_thread = threading.currentThread() dummy_thread = threading.current_thread()
dummy_thread.is_pydev_daemon_thread = False dummy_thread.is_pydev_daemon_thread = False
return ReaderThread._on_run(self) return ReaderThread._on_run(self)

View file

@ -18,7 +18,7 @@ try:
except: except:
from urllib.parse import quote # @UnresolvedImport from urllib.parse import quote # @UnresolvedImport
threadingCurrentThread = threading.currentThread threadingCurrentThread = threading.current_thread
DONT_TRACE_THREADING = ['threading.py', 'pydevd.py'] DONT_TRACE_THREADING = ['threading.py', 'pydevd.py']
INNER_METHODS = ['_stop'] INNER_METHODS = ['_stop']
@ -105,7 +105,7 @@ def send_concurrency_message(event_class, time, name, thread_id, type, event, fi
def log_new_thread(global_debugger, t): def log_new_thread(global_debugger, t):
event_time = cur_time() - global_debugger.thread_analyser.start_time event_time = cur_time() - global_debugger.thread_analyser.start_time
send_concurrency_message("threading_event", event_time, t.getName(), get_thread_id(t), "thread", send_concurrency_message("threading_event", event_time, t.name, get_thread_id(t), "thread",
"start", "code_name", 0, None, parent=get_thread_id(t)) "start", "code_name", 0, None, parent=get_thread_id(t))
@ -162,7 +162,7 @@ class ThreadingLogger:
if not self_obj.is_alive(): if not self_obj.is_alive():
return return
thread_id = get_thread_id(t) thread_id = get_thread_id(t)
name = t.getName() name = t.name
self_obj._pydev_join_called = True self_obj._pydev_join_called = True
if real_method == "start": if real_method == "start":
@ -200,7 +200,7 @@ class ThreadingLogger:
# back_back_base is the file, where the method was called froms # back_back_base is the file, where the method was called froms
return return
if method_name == "__init__": if method_name == "__init__":
send_concurrency_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", send_concurrency_message("threading_event", event_time, t.name, get_thread_id(t), "lock",
method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"]))) method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"])))
if "attr" in frame.f_locals and \ if "attr" in frame.f_locals and \
(frame.f_locals["attr"] in LOCK_METHODS or (frame.f_locals["attr"] in LOCK_METHODS or
@ -215,14 +215,14 @@ class ThreadingLogger:
if real_method == "release_end": if real_method == "release_end":
# do not log release end. Maybe use it later # do not log release end. Maybe use it later
return return
send_concurrency_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", send_concurrency_message("threading_event", event_time, t.name, get_thread_id(t), "lock",
real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
if real_method in ("put_end", "get_end"): if real_method in ("put_end", "get_end"):
# fake release for queue, cause we don't call it directly # fake release for queue, cause we don't call it directly
send_concurrency_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", send_concurrency_message("threading_event", event_time, t.name, get_thread_id(t), "lock",
"release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) "release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
# print(event_time, t.getName(), get_thread_id(t), "lock", # print(event_time, t.name, get_thread_id(t), "lock",
# real_method, back.f_code.co_filename, back.f_lineno) # real_method, back.f_code.co_filename, back.f_lineno)
except Exception: except Exception:

View file

@ -33,7 +33,7 @@ class Test(unittest.TestCase):
time.sleep(.3) # let's give it some time to start the threads time.sleep(.3) # let's give it some time to start the threads
from _pydev_bundle import pydev_localhost from _pydev_bundle import pydev_localhost
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread()) interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.current_thread())
(result,) = interpreter.hello("Hello pydevconsole") (result,) = interpreter.hello("Hello pydevconsole")
self.assertEqual(result, "Hello eclipse") self.assertEqual(result, "Hello eclipse")
@ -53,7 +53,7 @@ class Test(unittest.TestCase):
from _pydev_bundle import pydev_localhost from _pydev_bundle import pydev_localhost
from _pydev_bundle.pydev_console_utils import CodeFragment from _pydev_bundle.pydev_console_utils import CodeFragment
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread()) interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.current_thread())
sys.stdout = pydevd_io.IOBuf() sys.stdout = pydevd_io.IOBuf()
interpreter.add_exec(CodeFragment('class Foo:\n CONSTANT=1\n')) interpreter.add_exec(CodeFragment('class Foo:\n CONSTANT=1\n'))
interpreter.add_exec(CodeFragment('foo=Foo()')) interpreter.add_exec(CodeFragment('foo=Foo()'))

View file

@ -21,11 +21,11 @@ def get_marked_line_numbers(path):
print(1) # @foo print(1) # @foo
print(2) print(2)
print(3) # @bar print(3) # @bar,baz
the function will return:: the function will return::
{"foo": 1, "bar": 3} {"foo": 1, "bar": 3, "baz": 3}
""" """
if isinstance(path, py.path.local): if isinstance(path, py.path.local):
@ -40,10 +40,11 @@ def get_marked_line_numbers(path):
with open(path, "rb") as f: with open(path, "rb") as f:
lines = {} lines = {}
for i, line in enumerate(f): for i, line in enumerate(f):
match = re.search(br"#\s*@\s*(.+?)\s*$", line) match = re.search(br"#\s*@(.+?)\s*$", line)
if match: if match:
marker = compat.force_unicode(match.group(1), "ascii") markers = compat.force_unicode(match.group(1), "ascii")
lines[marker] = i + 1 for marker in markers.split(","):
lines[marker] = i + 1
_marked_line_numbers_cache[path] = lines _marked_line_numbers_cache[path] = lines
return lines return lines

View file

@ -328,46 +328,46 @@ def test_invalid_breakpoints(pyfile, target, run):
debuggee.setup() debuggee.setup()
# For markers below, rN = requested breakpoint, eN = expected breakpoint.
# If there's no eN for some rN, it's assumed to be the same line.
# fmt: off # fmt: off
b = True b = True
while b: # @bp1-expected while b: # @e0-27,e0-35,e0-36,e0-37,e0-38,e0-39
pass # @bp1-requested pass # @r0
break break
print() # @bp2-expected print() # @e1-27,e1-35,e1-36,e1-37
[ # @bp2-requested [ # @r1,e2
1, 2, 3, # @bp3-expected 1, 2, 3, # @e2-27,e2-35,e2-36,e2-37,e2-38
] # @bp3-requested ] # @r2
# Python 2.7 only. # Python 2.7 only.
print() # @bp4-expected print() # @e3,e4
print(1, # @bp4-requested-1 print(1, # @r3
2, 3, # @bp4-requested-2 2, 3, # @r4
4, 5, 6) 4, 5, 6)
# fmt: on # fmt: on
with debug.Session() as session: with debug.Session() as session:
with run(session, target(code_to_debug)): with run(session, target(code_to_debug)):
bp_markers = ["bp1-requested", "bp2-requested", "bp3-requested"] count = 5 if sys.version_info < (3,) else 3
if sys.version_info < (3,): requested_markers = ["r" + str(i) for i in range(0, count)]
bp_markers += ["bp4-requested-1", "bp4-requested-2"]
bps = session.set_breakpoints(code_to_debug, bp_markers) bps = session.set_breakpoints(code_to_debug, requested_markers)
actual_lines = [bp["line"] for bp in bps] actual_lines = [bp["line"] for bp in bps]
if sys.version_info >= (3, 9): expected_markers = []
expected_markers = ["bp1-expected", "bp2-requested", "bp2-requested"] for r in requested_markers:
elif sys.version_info >= (3, 8): e_generic = "e" + r[1:]
# See: https://bugs.python.org/issue38508 e_versioned = e_generic + "-" + str(sys.version_info.major) + str(sys.version_info.minor)
expected_markers = ["bp1-expected", "bp2-requested", "bp3-expected"] for e in e_versioned, e_generic, r:
else: if e in code_to_debug.lines:
expected_markers = ["bp1-expected", "bp2-expected", "bp3-expected"] expected_markers.append(e)
if sys.version_info < (3,): break
expected_markers += ["bp4-expected", "bp4-expected"]
expected_lines = [ expected_lines = [
code_to_debug.lines[marker] for marker in expected_markers code_to_debug.lines[marker] for marker in expected_markers
] ]
assert actual_lines == expected_lines assert actual_lines == expected_lines
# Now let's make sure that we hit all of the expected breakpoints, # Now let's make sure that we hit all of the expected breakpoints,
@ -377,10 +377,9 @@ def test_invalid_breakpoints(pyfile, target, run):
# so remove duplicates first. # so remove duplicates first.
expected_lines = sorted(set(expected_lines)) expected_lines = sorted(set(expected_lines))
if (3, 8) <= sys.version_info < (3, 9): if (3, 8) <= sys.version_info < (3, 9):
# We'll actually hit @bp3-expected and later @bp2-requested # We'll actually hit @e2-38 first, and only then @r1, because there's
# (there's a line event when the list creation is finished # a line event for [ when the list creation is finished on 3.8).
# at the start of the list creation on 3.8). # See https://bugs.python.org/issue38508 for details.
# See: https://bugs.python.org/issue38508
expected_lines[1], expected_lines[2] = expected_lines[2], expected_lines[1] expected_lines[1], expected_lines[2] = expected_lines[2], expected_lines[1]
while expected_lines: while expected_lines:

View file

@ -5,13 +5,17 @@
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import pytest import pytest
import sys
from debugpy.common import compat from debugpy.common import compat
from tests import code, debug, log, net, test_data from tests import code, debug, log, net, test_data
from tests.debug import runners, targets from tests.debug import runners, targets
from tests.patterns import some from tests.patterns import some
pytestmark = pytest.mark.timeout(60) pytestmark = [
pytest.mark.timeout(60),
pytest.mark.skipif(sys.version_info >= (3, 10), reason="https://github.com/microsoft/debugpy/issues/689"),
]
django_server = net.WebServer(net.get_test_server_port(8000, 8100)) django_server = net.WebServer(net.get_test_server_port(8000, 8100))

View file

@ -4,10 +4,14 @@
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import pytest
import sys
from tests import debug from tests import debug
from tests.patterns import some from tests.patterns import some
@pytest.mark.skipif(sys.version_info >= (3, 10), reason="https://github.com/microsoft/debugpy/issues/688")
def test_gevent(pyfile, target, run): def test_gevent(pyfile, target, run):
@pyfile @pyfile
def code_to_debug(): def code_to_debug():

View file

@ -1,11 +1,9 @@
setuptools>=57.4.0
## Used to run the tests: ## Used to run the tests:
# pytest>=5 does not support Python 2.7 pytest
pytest<5 pytest-xdist
# pytest-xdist>=2 does not support Python 2.7
pytest-xdist<2
pytest-cov pytest-cov
pytest-timeout pytest-timeout
tox tox
@ -19,5 +17,4 @@ psutil
django django
requests requests
gevent gevent
flask>=1.1.2 flask>=1.1.2

22
tests/requirements27.txt Normal file
View file

@ -0,0 +1,22 @@
## Used to run the tests:
# pytest>=5 does not support Python 2.7
pytest<5
# pytest-xdist>=2 does not support Python 2.7
pytest-xdist<2
pytest-cov
pytest-timeout
tox
## Used by test helpers:
psutil
## Used in Python code that is run/debugged by the tests:
django
requests
gevent
flask>=1.1.2

20
tests/requirements35.txt Normal file
View file

@ -0,0 +1,20 @@
setuptools
## Used to run the tests:
pytest
pytest-xdist
pytest-cov
pytest-timeout
tox
## Used by test helpers:
psutil
## Used in Python code that is run/debugged by the tests:
django
requests
gevent
flask>=1.1.2

View file

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py{27,35,36,37,38}{,-cov} envlist = py{27,35,36,37,38,39,310}{,-cov}
[testenv] [testenv]
deps = -rtests/requirements.txt deps = -rtests/requirements.txt
@ -9,3 +9,9 @@ setenv =
commands = commands =
!cov: pytest {posargs} !cov: pytest {posargs}
cov: pytest --cov --cov-append --cov-config=.coveragerc {posargs} cov: pytest --cov --cov-append --cov-config=.coveragerc {posargs}
[testenv:py27]
deps = -rtests/requirements27.txt
[testenv:py35]
deps = -rtests/requirements35.txt