Updates from PyDev.Debugger: 1.0.0 release (PY-22904)

This commit is contained in:
Elizaveta Shashkova
2017-03-09 15:20:47 +03:00
parent 326cf9629f
commit d6e8ff2e92
17 changed files with 927 additions and 415 deletions
@@ -21,24 +21,14 @@ def log_error_once(msg):
pydev_src_dir = os.path.dirname(os.path.dirname(__file__))
def _get_pydevd_args():
new_args = []
for x in sys.original_argv:
new_args.append(x)
if x == '--file':
break
return new_args
def _get_python_c_args(host, port, indC, args):
host_literal = "'" + host + "'" if host is not None else 'None'
return ("import sys; sys.path.append(r'%s'); import pydevd; "
"pydevd.settrace(host=%s, port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); "
"sys.original_argv = %s; %s"
"pydevd.settrace(host='%s', port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); "
"%s"
) % (
pydev_src_dir,
host_literal,
host,
port,
_get_pydevd_args(),
args[indC + 1])
def _get_host_port():
@@ -155,7 +145,9 @@ def patch_args(args):
# Original args should be something as:
# ['X:\\pysrc\\pydevd.py', '--multiprocess', '--print-in-debugger-startup',
# '--vm_type', 'python', '--client', '127.0.0.1', '--port', '56352', '--file', 'x:\\snippet1.py']
original = sys.original_argv[:]
from _pydevd_bundle.pydevd_command_line_handling import setup_to_argv
from pydevd import SetupHolder
original = setup_to_argv(SetupHolder.setup) + ['--file']
while i < len(args):
if args[i] == '-m':
# Always insert at pos == 1 (i.e.: pydevd "--module" --multiprocess ...)
@@ -1,39 +1,61 @@
import pickle, zlib, base64, os
import py
from _pydev_runfiles import pydev_runfiles_xml_rpc
import pickle
import zlib
import base64
import os
import py
from pydevd_file_utils import _NormFile
import pytest
import sys
import time
#===================================================================================================
#=========================================================================
# Load filters with tests we should skip
#===================================================================================================
#=========================================================================
py_test_accept_filter = None
def _load_filters():
global py_test_accept_filter
if py_test_accept_filter is None:
py_test_accept_filter = os.environ.get('PYDEV_PYTEST_SKIP')
if py_test_accept_filter:
py_test_accept_filter = pickle.loads(zlib.decompress(base64.b64decode(py_test_accept_filter)))
py_test_accept_filter = pickle.loads(
zlib.decompress(base64.b64decode(py_test_accept_filter)))
else:
py_test_accept_filter = {}
def connect_to_server_for_communication_to_xml_rpc_on_xdist():
def is_in_xdist_node():
main_pid = os.environ.get('PYDEV_MAIN_PID')
if main_pid and main_pid != str(os.getpid()):
return True
return False
connected = False
def connect_to_server_for_communication_to_xml_rpc_on_xdist():
global connected
if connected:
return
connected = True
if is_in_xdist_node():
port = os.environ.get('PYDEV_PYTEST_SERVER')
if not port:
sys.stderr.write('Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
sys.stderr.write(
'Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
else:
pydev_runfiles_xml_rpc.initialize_server(int(port), daemon=True)
PY2 = sys.version_info[0] <= 2
PY3 = not PY2
#=========================================================================
# Mocking to get clickable file representations
#=========================================================================
_mock_code = []
try:
from py._code import code # @UnresolvedImport
@@ -46,14 +68,10 @@ try:
except ImportError:
pass
#===================================================================================================
# Mocking to get clickable file representations
#===================================================================================================
def _MockFileRepresentation():
for code in _mock_code:
code.ReprFileLocation._original_toterminal = code.ReprFileLocation.toterminal
def toterminal(self, tw):
# filename and lineno output for each entry,
# using an output format that most editors understand
@@ -65,12 +83,15 @@ def _MockFileRepresentation():
path = os.path.abspath(self.path)
if PY2:
if not isinstance(path, unicode): # Note: it usually is NOT unicode...
# Note: it usually is NOT unicode...
if not isinstance(path, unicode):
path = path.decode(sys.getfilesystemencoding(), 'replace')
if not isinstance(msg, unicode): # Note: it usually is unicode...
# Note: it usually is unicode...
if not isinstance(msg, unicode):
msg = msg.decode('utf-8', 'replace')
unicode_line = unicode('File "%s", line %s\n%s') % (path, self.lineno, msg)
unicode_line = unicode('File "%s", line %s\n%s') % (
path, self.lineno, msg)
tw.line(unicode_line)
else:
tw.line('File "%s", line %s\n%s' % (path, self.lineno, msg))
@@ -80,209 +101,238 @@ def _MockFileRepresentation():
def _UninstallMockFileRepresentation():
for code in _mock_code:
code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal #@UndefinedVariable
# @UndefinedVariable
code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal
#=========================================================================
# End mocking to get clickable file representations
#=========================================================================
class State:
numcollected = 0
start_time = time.time()
buf_err = None
buf_out = None
def pytest_configure(*args, **kwargs):
def start_redirect():
if State.buf_out is not None:
return
from _pydevd_bundle import pydevd_io
State.buf_err = pydevd_io.start_redirect(keep_original_redirection=True, std='stderr')
State.buf_out = pydevd_io.start_redirect(keep_original_redirection=True, std='stdout')
def get_curr_output():
return State.buf_out.getvalue(), State.buf_err.getvalue()
def pytest_configure():
_MockFileRepresentation()
def pytest_collectreport(report):
i = 0
for x in report.result:
if isinstance(x, pytest.Item):
try:
# Call our setup (which may do a skip, in which
# case we won't count it).
pytest_runtest_setup(x)
i += 1
except:
continue
State.numcollected += i
def pytest_collection_modifyitems():
connect_to_server_for_communication_to_xml_rpc_on_xdist()
pydev_runfiles_xml_rpc.notifyTestsCollected(State.numcollected)
State.numcollected = 0
def pytest_unconfigure(*args, **kwargs):
def pytest_unconfigure():
_UninstallMockFileRepresentation()
pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - State.start_time,))
if is_in_xdist_node():
return
# Only report that it finished when on the main node (we don't want to report
# the finish on each separate node).
pydev_runfiles_xml_rpc.notifyTestRunFinished(
'Finished in: %.2f secs.' % (time.time() - State.start_time,))
def pytest_runtest_setup(item):
filename = item.fspath.strpath
test = item.location[2]
State.start_test_time = time.time()
def pytest_collection_modifyitems(session, config, items):
# A note: in xdist, this is not called on the main process, only in the
# secondary nodes, so, we'll actually make the filter and report it multiple
# times.
connect_to_server_for_communication_to_xml_rpc_on_xdist()
pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
_load_filters()
if not py_test_accept_filter:
pydev_runfiles_xml_rpc.notifyTestsCollected(len(items))
return # Keep on going (nothing to filter)
new_items = []
for item in items:
f = _NormFile(str(item.parent.fspath))
name = item.name
def report_test(cond, filename, test, captured_output, error_contents, delta):
'''
@param filename: 'D:\\src\\mod1\\hello.py'
@param test: 'TestCase.testMet1'
@param cond: fail, error, ok
'''
time_str = '%.2f' % (delta,)
pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, filename, test, time_str)
if f not in py_test_accept_filter:
# print('Skip file: %s' % (f,))
continue # Skip the file
accept_tests = py_test_accept_filter[f]
def pytest_runtest_makereport(item, call):
report_when = call.when
report_duration = call.stop-call.start
excinfo = call.excinfo
if not call.excinfo:
evalxfail = getattr(item, '_evalxfail', None)
if evalxfail and report_when == 'call' and (not hasattr(evalxfail, 'expr') or evalxfail.expr):
# I.e.: a method marked with xfail passed... let the user know.
report_outcome = "failed"
report_longrepr = "XFAIL: Unexpected pass"
if item.cls is not None:
class_name = item.cls.__name__
else:
report_outcome = "passed"
report_longrepr = None
else:
excinfo = call.excinfo
class_name = None
for test in accept_tests:
# This happens when parameterizing pytest tests.
i = name.find('[')
if i > 0:
name = name[:i]
if test == name:
# Direct match of the test (just go on with the default
# loading)
new_items.append(item)
break
handled = False
if class_name is not None:
if test == class_name + '.' + name:
new_items.append(item)
break
if not (call.excinfo and
call.excinfo.errisinstance(pytest.xfail.Exception)):
evalxfail = getattr(item, '_evalxfail', None)
# Something which had an xfail failed: this is expected.
if evalxfail and (not hasattr(evalxfail, 'expr') or evalxfail.expr):
report_outcome = "passed"
report_longrepr = None
handled = True
if handled:
if class_name == test:
new_items.append(item)
break
else:
pass
# print('Skip test: %s.%s. Accept: %s' % (class_name, name, accept_tests))
if excinfo.errisinstance(pytest.xfail.Exception):
# Case where an explicit xfail is raised (i.e.: pytest.xfail("reason") is called
# programatically).
report_outcome = "passed"
report_longrepr = None
# Modify the original list
items[:] = new_items
pydev_runfiles_xml_rpc.notifyTestsCollected(len(items))
elif excinfo.errisinstance(py.test.skip.Exception): # @UndefinedVariable
report_outcome = "skipped"
r = excinfo._getreprcrash()
report_longrepr = None #(str(r.path), r.lineno, r.message)
elif not isinstance(excinfo, py.code.ExceptionInfo): # @UndefinedVariable
report_outcome = "failed"
report_longrepr = excinfo
from py.io import TerminalWriter
else:
report_outcome = "failed"
if call.when == "call":
report_longrepr = item.repr_failure(excinfo)
def _get_error_contents_from_report(report):
if report.longrepr is not None:
tw = TerminalWriter(stringio=True)
tw.hasmarkup = False
report.toterminal(tw)
exc = tw.stringio.getvalue()
s = exc.strip()
if s:
return s
else: # exception in setup or teardown
report_longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle)
return ''
filename = item.fspath.strpath
test = item.location[2]
def pytest_collectreport(report):
error_contents = _get_error_contents_from_report(report)
if error_contents:
report_test('fail', '<collect errors>', '<collect errors>', '', error_contents, 0.0)
status = 'ok'
captured_output = ''
error_contents = ''
def append_strings(s1, s2):
if s1.__class__ == s2.__class__:
return s1 + s2
if report_outcome in ('passed', 'skipped'):
#passed or skipped: no need to report if in setup or teardown (only on the actual test if it passed).
if sys.version_info[0] == 2:
if not isinstance(s1, basestring):
s1 = str(s1)
if not isinstance(s2, basestring):
s2 = str(s2)
# Prefer bytes
if isinstance(s1, unicode):
s1 = s1.encode('utf-8')
if isinstance(s2, unicode):
s2 = s2.encode('utf-8')
return s1 + s2
else:
# Prefer str
if isinstance(s1, bytes):
s1 = s1.decode('utf-8', 'replace')
if isinstance(s2, bytes):
s2 = s2.decode('utf-8', 'replace')
return s1 + s2
def pytest_runtest_logreport(report):
if is_in_xdist_node():
# When running with xdist, we don't want the report to be called from the node, only
# from the main process.
return
report_duration = report.duration
report_when = report.when
report_outcome = report.outcome
if hasattr(report, 'wasxfail'):
if report_outcome != 'skipped':
report_outcome = 'passed'
if report_outcome == 'passed':
# passed on setup/teardown: no need to report if in setup or teardown
# (only on the actual test if it passed).
if report_when in ('setup', 'teardown'):
return
else:
#It has only passed, skipped and failed (no error), so, let's consider error if not on call.
if report_when == 'setup':
if status == 'ok':
status = 'error'
status = 'ok'
elif report_when == 'teardown':
if status == 'ok':
status = 'error'
elif report_outcome == 'skipped':
status = 'skip'
else:
# It has only passed, skipped and failed (no error), so, let's consider
# error if not on call.
if report_when in ('setup', 'teardown'):
status = 'error'
else:
#any error in the call (not in setup or teardown) is considered a regular failure.
# any error in the call (not in setup or teardown) is considered a
# regular failure.
status = 'fail'
# This will work if pytest is not capturing it, if it is, nothing will
# come from here...
captured_output, error_contents = report.pydev_captured_output, report.pydev_error_contents
for type_section, value in report.sections:
if value:
if type_section in ('err', 'stderr', 'Captured stderr call'):
error_contents = append_strings(error_contents, value)
else:
captured_output = append_strings(error_contents, value)
if call.excinfo:
rep = report_longrepr
if hasattr(rep, 'reprcrash'):
reprcrash = rep.reprcrash
error_contents += str(reprcrash)
error_contents += '\n'
filename = report.pydev_fspath_strpath
test = report.location[2]
if hasattr(rep, 'reprtraceback'):
error_contents += str(rep.reprtraceback)
if report_outcome != 'skipped':
# On skipped, we'll have a traceback for the skip, which is not what we
# want.
exc = _get_error_contents_from_report(report)
if exc:
if error_contents:
error_contents = append_strings(error_contents, '----------------------------- Exceptions -----------------------------\n')
error_contents = append_strings(error_contents, exc)
if hasattr(rep, 'sections'):
for name, content, sep in rep.sections:
error_contents += sep * 40
error_contents += name
error_contents += sep * 40
error_contents += '\n'
error_contents += content
error_contents += '\n'
else:
if report_longrepr:
error_contents += str(report_longrepr)
report_test(status, filename, test, captured_output, error_contents, report_duration)
if status != 'skip': #I.e.: don't event report skips...
report_test(status, filename, test, captured_output, error_contents, report_duration)
def report_test(status, filename, test, captured_output, error_contents, duration):
'''
@param filename: 'D:\\src\\mod1\\hello.py'
@param test: 'TestCase.testMet1'
@param status: fail, error, ok
'''
time_str = '%.2f' % (duration,)
pydev_runfiles_xml_rpc.notifyTest(
status, captured_output, error_contents, filename, test, time_str)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.pydev_fspath_strpath = item.fspath.strpath
report.pydev_captured_output, report.pydev_error_contents = get_curr_output()
@pytest.mark.tryfirst
def pytest_runtest_setup(item): # @DuplicatedSignature
def pytest_runtest_setup(item):
'''
Skips tests. With xdist will be on a secondary process.
Note: with xdist will be on a secondary process.
'''
_load_filters()
if not py_test_accept_filter:
return #Keep on going (nothing to filter)
f = _NormFile(str(item.parent.fspath))
name = item.name
if f not in py_test_accept_filter:
pytest.skip() # Skip the file
accept_tests = py_test_accept_filter[f]
if item.cls is not None:
class_name = item.cls.__name__
else:
class_name = None
for test in accept_tests:
# This happens when parameterizing pytest tests.
i = name.find('[')
if i > 0:
name = name[:i]
if test == name:
#Direct match of the test (just go on with the default loading)
return
if class_name is not None:
if test == class_name + '.' + name:
return
if class_name == test:
return
# If we had a match it'd have returned already.
pytest.skip() # Skip the test
# We have our own redirection: if xdist does its redirection, we'll have
# nothing in our contents (which is OK), but if it does, we'll get nothing
# from pytest but will get our own here.
start_redirect()
filename = item.fspath.strpath
test = item.location[2]
pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
@@ -26,6 +26,17 @@ _PythonTextTestResult = python_unittest.TextTestRunner()._makeResult().__class__
#=======================================================================================================================
class PydevTestResult(_PythonTextTestResult):
def addSubTest(self, test, subtest, err):
"""Called at the end of a subtest.
'err' is None if the subtest ended successfully, otherwise it's a
tuple of values as returned by sys.exc_info().
"""
_PythonTextTestResult.addSubTest(self, test, subtest, err)
if err is not None:
subdesc = subtest._subDescription()
error = (test, self._exc_info_to_string(err, test))
self._reportErrors([error], [], '', '%s %s' % (self.get_test_name(test), subdesc))
def startTest(self, test):
_PythonTextTestResult.startTest(self, test)
@@ -0,0 +1,145 @@
class ArgHandlerWithParam:
'''
Handler for some arguments which needs a value
'''
def __init__(self, arg_name, convert_val=None, default_val=None):
self.arg_name = arg_name
self.arg_v_rep = '--%s' % (arg_name,)
self.convert_val = convert_val
self.default_val = default_val
def to_argv(self, lst, setup):
v = setup.get(self.arg_name)
if v is not None and v != self.default_val:
lst.append(self.arg_v_rep)
lst.append('%s' % (v,))
def handle_argv(self, argv, i, setup):
assert argv[i] == self.arg_v_rep
del argv[i]
val = argv[i]
if self.convert_val:
val = self.convert_val(val)
setup[self.arg_name] = val
del argv[i]
class ArgHandlerBool:
'''
If a given flag is received, mark it as 'True' in setup.
'''
def __init__(self, arg_name, default_val=False):
self.arg_name = arg_name
self.arg_v_rep = '--%s' % (arg_name,)
self.default_val = default_val
def to_argv(self, lst, setup):
v = setup.get(self.arg_name)
if v:
lst.append(self.arg_v_rep)
def handle_argv(self, argv, i, setup):
assert argv[i] == self.arg_v_rep
del argv[i]
setup[self.arg_name] = True
ACCEPTED_ARG_HANDLERS = [
ArgHandlerWithParam('port', int, 0),
ArgHandlerWithParam('vm_type'),
ArgHandlerWithParam('client'),
ArgHandlerBool('server'),
ArgHandlerBool('DEBUG_RECORD_SOCKET_READS'),
ArgHandlerBool('multiproc'), # Used by PyCharm (reuses connection: ssh tunneling)
ArgHandlerBool('multiprocess'), # Used by PyDev (creates new connection to ide)
ArgHandlerBool('save-signatures'),
ArgHandlerBool('save-threading'),
ArgHandlerBool('save-asyncio'),
ArgHandlerBool('print-in-debugger-startup'),
ArgHandlerBool('cmd-line'),
ArgHandlerBool('module'),
]
ARGV_REP_TO_HANDLER = {}
for handler in ACCEPTED_ARG_HANDLERS:
ARGV_REP_TO_HANDLER[handler.arg_v_rep] = handler
def get_pydevd_file():
import pydevd
f = pydevd.__file__
if f.endswith('.pyc'):
f = f[:-1]
return f
def setup_to_argv(setup):
'''
:param dict setup:
A dict previously gotten from process_command_line.
:note: does not handle --file nor --DEBUG.
'''
ret = [get_pydevd_file()]
for handler in ACCEPTED_ARG_HANDLERS:
if handler.arg_name in setup:
handler.to_argv(ret, setup)
return ret
def process_command_line(argv):
""" parses the arguments.
removes our arguments from the command line """
setup = {}
for handler in ACCEPTED_ARG_HANDLERS:
setup[handler.arg_name] = handler.default_val
setup['file'] = ''
setup['qt-support'] = ''
i = 0
del argv[0]
while i < len(argv):
handler = ARGV_REP_TO_HANDLER.get(argv[i])
if handler is not None:
handler.handle_argv(argv, i, setup)
elif argv[i].startswith('--qt-support'):
# The --qt-support is special because we want to keep backward compatibility:
# Previously, just passing '--qt-support' meant that we should use the auto-discovery mode
# whereas now, if --qt-support is passed, it should be passed as --qt-support=<mode>, where
# mode can be one of 'auto', 'none', 'pyqt5', 'pyqt4', 'pyside'.
if argv[i] == '--qt-support':
setup['qt-support'] = 'auto'
elif argv[i].startswith('--qt-support='):
qt_support = argv[i][len('--qt-support='):]
valid_modes = ('none', 'auto', 'pyqt5', 'pyqt4', 'pyside')
if qt_support not in valid_modes:
raise ValueError("qt-support mode invalid: " + qt_support)
if qt_support == 'none':
# On none, actually set an empty string to evaluate to False.
setup['qt-support'] = ''
else:
setup['qt-support'] = qt_support
else:
raise ValueError("Unexpected definition for qt-support flag: " + argv[i])
del argv[i]
elif argv[i] == '--file':
# --file is special because it's the last one (so, no handler for it).
del argv[i]
setup['file'] = argv[i]
i = len(argv) # pop out, file is our last argument
elif argv[i] == '--DEBUG':
from pydevd import set_debug
del argv[i]
set_debug(setup)
else:
raise ValueError("Unexpected option: " + argv[i])
return setup
@@ -65,6 +65,7 @@ DONT_TRACE = {
'pydevd_additional_thread_info_regular.py': PYDEV_FILE,
'pydevd_breakpoints.py': PYDEV_FILE,
'pydevd_comm.py': PYDEV_FILE,
'pydevd_command_line_handling.py': PYDEV_FILE,
'pydevd_concurrency_logger.py': PYDEV_FILE,
'pydevd_console.py': PYDEV_FILE,
'pydevd_constants.py': PYDEV_FILE,
+32 -88
View File
@@ -42,7 +42,7 @@ from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogge
from pydevd_concurrency_analyser.pydevd_thread_wrappers import wrap_threads
__version_info__ = (0, 0, 5)
__version_info__ = (1, 0, 0)
__version_info_str__ = []
for v in __version_info__:
__version_info_str__.append(str(v))
@@ -931,8 +931,10 @@ class PyDB:
return meth(mod_name)
return None
def run(self, file, globals=None, locals=None, module=False, set_trace=True):
if module:
def run(self, file, globals=None, locals=None, is_module=False, set_trace=True):
module_name = None
if is_module:
module_name = file
filename = self.get_fullname(file)
if filename is None:
sys.stderr.write("No module named %s\n" % file)
@@ -971,11 +973,12 @@ class PyDB:
# print >> sys.stderr, 'Deleting: ', sys.path[0]
del sys.path[0]
# now, the local directory has to be added to the pythonpath
# sys.path.insert(0, os.getcwd())
# Changed: it's not the local directory, but the directory of the file launched
# The file being run ust be in the pythonpath (even if it was not before)
sys.path.insert(0, os.path.split(file)[0])
if not is_module:
# now, the local directory has to be added to the pythonpath
# sys.path.insert(0, os.getcwd())
# Changed: it's not the local directory, but the directory of the file launched
# The file being run must be in the pythonpath (even if it was not before)
sys.path.insert(0, os.path.split(file)[0])
while not self.ready_to_run:
time.sleep(0.1) # busy wait until we receive run command
@@ -1005,7 +1008,16 @@ class PyDB:
sys.stderr.write("Matplotlib support in debugger failed\n")
traceback.print_exc()
pydev_imports.execfile(file, globals, locals) # execute the script
if not is_module:
pydev_imports.execfile(file, globals, locals) # execute the script
else:
# Run with the -m switch
import runpy
if hasattr(runpy, '_run_module_as_main'):
# Newer versions of Python actually use this when the -m switch is used.
runpy._run_module_as_main(module_name, alter_argv=False)
else:
runpy.run_module(module_name)
return globals
def exiting(self):
@@ -1050,86 +1062,9 @@ def enable_qt_support():
pydev_monkey_qt.patch_qt()
def process_command_line(argv):
""" parses the arguments.
removes our arguments from the command line """
setup = {}
setup['client'] = None
setup['server'] = False
setup['port'] = 0
setup['file'] = ''
setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling)
setup['multiprocess'] = False # Used by PyDev (creates new connection to ide)
setup['save-signatures'] = False
setup['save-threading'] = False
setup['save-asyncio'] = False
setup['qt-support'] = False
setup['print-in-debugger-startup'] = False
setup['cmd-line'] = False
setup['module'] = False
i = 0
del argv[0]
while (i < len(argv)):
if argv[i] == '--port':
del argv[i]
setup['port'] = int(argv[i])
del argv[i]
elif argv[i] == '--vm_type':
del argv[i]
setup['vm_type'] = argv[i]
del argv[i]
elif argv[i] == '--client':
del argv[i]
setup['client'] = argv[i]
del argv[i]
elif argv[i] == '--server':
del argv[i]
setup['server'] = True
elif argv[i] == '--file':
del argv[i]
setup['file'] = argv[i]
i = len(argv) # pop out, file is our last argument
elif argv[i] == '--DEBUG_RECORD_SOCKET_READS':
del argv[i]
setup['DEBUG_RECORD_SOCKET_READS'] = True
elif argv[i] == '--DEBUG':
del argv[i]
set_debug(setup)
elif argv[i] == '--multiproc':
del argv[i]
setup['multiproc'] = True
elif argv[i] == '--multiprocess':
del argv[i]
setup['multiprocess'] = True
elif argv[i] == '--save-signatures':
del argv[i]
setup['save-signatures'] = True
elif argv[i] == '--save-threading':
del argv[i]
setup['save-threading'] = True
elif argv[i] == '--save-asyncio':
del argv[i]
setup['save-asyncio'] = True
elif argv[i] == '--qt-support':
del argv[i]
setup['qt-support'] = True
elif argv[i] == '--print-in-debugger-startup':
del argv[i]
setup['print-in-debugger-startup'] = True
elif (argv[i] == '--cmd-line'):
del argv[i]
setup['cmd-line'] = True
elif (argv[i] == '--module'):
del argv[i]
setup['module'] = True
else:
raise ValueError("unexpected option " + argv[i])
return setup
def usage(doExit=0):
sys.stdout.write('Usage:\n')
sys.stdout.write('pydevd.py --port=N [(--client hostname) | --server] --file executable [file_options]\n')
sys.stdout.write('pydevd.py --port N [(--client hostname) | --server] --file executable [file_options]\n')
if doExit:
sys.exit(0)
@@ -1237,6 +1172,15 @@ def _locked_settrace(
if not connected :
pydevd_vm_type.setup_type()
if SetupHolder.setup is None:
setup = {
'client': host, # dispatch expects client to be set to the host address when server is False
'server': False,
'port': int(port),
'multiprocess': patch_multiprocessing,
}
SetupHolder.setup = setup
debugger = PyDB()
debugger.connect(host, port) # Note: connect can raise error.
@@ -1467,7 +1411,7 @@ if __name__ == '__main__':
# parse the command line. --file is our last argument that is required
try:
sys.original_argv = sys.argv[:]
from _pydevd_bundle.pydevd_command_line_handling import process_command_line
setup = process_command_line(sys.argv)
SetupHolder.setup = setup
except ValueError:
@@ -280,8 +280,8 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh
if is_64 != is_python_64bit():
raise RuntimeError("The architecture of the Python used to connect doesn't match the architecture of the target.\n"
"Target 64 bits: %s\n"
"Current Python 64 bits: %s" % (is_64, is_python_64bit()))
"Target 64 bits: %s\n"
"Current Python 64 bits: %s" % (is_64, is_python_64bit()))
print('Connecting to %s bits target' % (bits,))
assert resolve_label(process, compat.b('PyGILState_Ensure'))
@@ -366,16 +366,16 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh
# Uncomment to see the disassembled version of what we just did...
# with open('f.asm', 'wb') as stream:
# stream.write(code)
#
# exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe'
# if is_64:
# arch = '64'
# else:
# arch = '32'
#
# subprocess.call((exe + ' -b %s f.asm' % arch).split())
# with open('f.asm', 'wb') as stream:
# stream.write(code)
#
# exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe'
# if is_64:
# arch = '64'
# else:
# arch = '32'
#
# subprocess.call((exe + ' -b %s f.asm' % arch).split())
print('Injecting code to target process')
thread, _thread_address = process.inject_code(code, 0)
@@ -414,7 +414,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
print('Attaching with arch: %s'% (arch,))
target_dll = os.path.join(filedir, 'attach_linux_%s.so' % suffix)
target_dll = os.path.normpath(target_dll)
target_dll = os.path.abspath(os.path.normpath(target_dll))
if not os.path.exists(target_dll):
raise RuntimeError('Could not find dll file to inject: %s' % target_dll)
@@ -428,11 +428,11 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
'--nw', # no gui interface
'--nh', # no ~/.gdbinit
'--nx', # no .gdbinit
# '--quiet', # no version number on startup
# '--quiet', # no version number on startup
'--pid',
str(pid),
'--batch',
# '--batch-silent',
# '--batch-silent',
]
cmd.extend(["--eval-command='set scheduler-locking off'"]) # If on we'll deadlock.
@@ -449,7 +449,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
if connect_debugger_tracing:
cmd.extend([
"--command='%s'" % (gdb_threads_settrace_file,),
])
])
#print ' '.join(cmd)
@@ -527,8 +527,8 @@ def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_d
"-o 'process attach --pid %d'"%pid,
"-o 'command script import \"%s\"'" % (lldb_prepare_file,),
"-o 'load_lib_and_attach \"%s\" %s \"%s\" %s'" % (target_dll,
is_debug, python_code, show_debug_info),
])
is_debug, python_code, show_debug_info),
])
if connect_debugger_tracing:
@@ -556,7 +556,7 @@ def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_d
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
)
print('Running lldb in target process.')
out, err = p.communicate()
print('stdout: %s' % (out,))
+1
View File
@@ -104,6 +104,7 @@ args = dict(
'pydevconsole',
'pydevd_file_utils',
'pydevd',
'pydevd_tracing',
# 'runfiles', -- Not needed for debugger
# 'setup_cython', -- Should not be included as a module
# 'setup', -- Should not be included as a module
@@ -0,0 +1,3 @@
from . import _debugger_case_m_switch_2
print(_debugger_case_m_switch_2.ClassToBeImported)
print('TEST SUCEEDED!')
@@ -0,0 +1,2 @@
class ClassToBeImported(object):
pass
@@ -0,0 +1,14 @@
if __name__ == '__main__':
import os
import sys
root_dirname = os.path.dirname(os.path.dirname(__file__))
if root_dirname not in sys.path:
sys.path.append(root_dirname)
import pydevd
print('before pydevd.settrace')
pydevd.settrace(port=8787)
print('after pydevd.settrace')
print('TEST SUCEEDED!')
@@ -0,0 +1,25 @@
if __name__ == '__main__':
import subprocess
import sys
import os
import _debugger_case_remote_2
root_dirname = os.path.dirname(os.path.dirname(__file__))
if root_dirname not in sys.path:
sys.path.append(root_dirname)
import pydevd
print('before pydevd.settrace')
sys.stdout.flush()
pydevd.settrace(port=8787, patch_multiprocessing=True)
print('after pydevd.settrace')
sys.stdout.flush()
f = _debugger_case_remote_2.__file__
if f.endswith('.pyc'):
f = f[:-1]
print('before call')
sys.stdout.flush()
subprocess.check_call([sys.executable, '-u', f])
print('after call')
sys.stdout.flush()
@@ -0,0 +1,11 @@
if __name__ == '__main__':
print('Run as main: %s' % (__file__,))
import sys
sys.stdout.flush()
import pydevd
# Just check that we're already connected
assert pydevd.GetGlobalDebugger() is not None
print('finish')
sys.stdout.flush()
print('TEST SUCEEDED!')
sys.stdout.flush()
@@ -103,12 +103,13 @@ class ReaderThread(threading.Thread):
self.sock = sock
self.last_received = ''
self.all_received = []
self._kill = False
def run(self):
last_printed = None
try:
buf = ''
while True:
while not self._kill:
l = self.sock.recv(1024)
if IS_PY3K:
l = l.decode('utf-8')
@@ -127,9 +128,13 @@ class ReaderThread(threading.Thread):
print('Test Reader Thread Received %s' % last_printed)
except:
pass # ok, finished it
finally:
del self.all_received[:]
def do_kill(self):
self.sock.close()
self._kill = True
if hasattr(self, 'sock'):
self.sock.close()
class DebuggerRunner(object):
@@ -145,7 +150,7 @@ class DebuggerRunner(object):
port = int(writer_thread.port)
localhost = pydev_localhost.get_localhost()
return args + [
ret = args + [
writer_thread.get_pydevd_file(),
'--DEBUG_RECORD_SOCKET_READS',
'--qt-support',
@@ -153,25 +158,40 @@ class DebuggerRunner(object):
localhost,
'--port',
str(port),
'--file',
] + writer_thread.get_command_line_args()
return args
]
if writer_thread.IS_MODULE:
ret += ['--module']
ret = ret + ['--file'] + writer_thread.get_command_line_args()
return ret
def check_case(self, writer_thread_class):
writer_thread = writer_thread_class()
writer_thread.start()
while not hasattr(writer_thread, 'port'):
time.sleep(.01)
self.writer_thread = writer_thread
try:
writer_thread.start()
for _i in xrange(40000):
if hasattr(writer_thread, 'port'):
break
time.sleep(.01)
self.writer_thread = writer_thread
args = self.get_command_line()
args = self.get_command_line()
args = self.add_command_line_args(args)
args = self.add_command_line_args(args)
if SHOW_OTHER_DEBUG_INFO:
print('executing', ' '.join(args))
if SHOW_OTHER_DEBUG_INFO:
print('executing', ' '.join(args))
return self.run_process(args, writer_thread)
ret = self.run_process(args, writer_thread)
finally:
writer_thread.do_kill()
writer_thread.log = []
stdout = ret['stdout']
stderr = ret['stderr']
writer_thread.additional_output_checks(''.join(stdout), ''.join(stderr))
return ret
def create_process(self, args, writer_thread):
process = subprocess.Popen(
@@ -187,78 +207,88 @@ class DebuggerRunner(object):
process = self.create_process(args, writer_thread)
stdout = []
stderr = []
finish = [False]
def read(stream, buffer):
for line in stream.readlines():
if IS_PY3K:
line = line.decode('utf-8')
try:
def read(stream, buffer):
for line in stream.readlines():
if finish[0]:
return
if IS_PY3K:
line = line.decode('utf-8')
if SHOW_STDOUT:
sys.stdout.write('stdout: %s' % (line,))
buffer.append(line)
if SHOW_STDOUT:
sys.stdout.write('stdout: %s' % (line,))
buffer.append(line)
start_new_thread(read, (process.stdout, stdout))
start_new_thread(read, (process.stdout, stdout))
if SHOW_OTHER_DEBUG_INFO:
print('Both processes started')
if SHOW_OTHER_DEBUG_INFO:
print('Both processes started')
# polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
# finish successfully).
check = 0
while True:
if process.poll() is not None:
break
else:
if writer_thread is not None:
if not writer_thread.isAlive():
if writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
process.kill()
continue
# polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
# finish successfully).
check = 0
while True:
if process.poll() is not None:
break
else:
if writer_thread is not None:
if not writer_thread.isAlive():
if writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
process.kill()
continue
check += 1
if check == 20:
print('Warning: writer thread exited and process still did not.')
if check == 100:
process.kill()
time.sleep(.2)
self.fail_with_message(
"The other process should've exited but still didn't (timeout for process to exit).",
stdout, stderr, writer_thread
)
time.sleep(.2)
if writer_thread is not None:
if not writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
poll = process.poll()
if poll < 0:
self.fail_with_message(
"The other process exited with error code: " + str(poll), stdout, stderr, writer_thread)
if stdout is None:
self.fail_with_message(
"The other process may still be running -- and didn't give any output.", stdout, stderr, writer_thread)
check = 0
while 'TEST SUCEEDED' not in ''.join(stdout):
check += 1
if check == 20:
print('Warning: writer thread exited and process still did not.')
if check == 100:
process.kill()
time.sleep(.2)
self.fail_with_message(
"The other process should've exited but still didn't (timeout for process to exit).",
stdout, stderr, writer_thread
)
time.sleep(.2)
if check == 50:
self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writer_thread)
time.sleep(.1)
for _i in xrange(100):
if not writer_thread.finished_ok:
time.sleep(.1)
if writer_thread is not None:
if not writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
poll = process.poll()
if poll < 0:
self.fail_with_message(
"The other process exited with error code: " + str(poll), stdout, stderr, writer_thread)
if stdout is None:
self.fail_with_message(
"The other process may still be running -- and didn't give any output.", stdout, stderr, writer_thread)
if 'TEST SUCEEDED' not in ''.join(stdout):
self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writer_thread)
for i in xrange(100):
if not writer_thread.finished_ok:
time.sleep(.1)
if not writer_thread.finished_ok:
self.fail_with_message(
"The thread that was doing the tests didn't finish successfully.", stdout, stderr, writer_thread)
self.fail_with_message(
"The thread that was doing the tests didn't finish successfully.", stdout, stderr, writer_thread)
finally:
finish[0] = True
return {'stdout':stdout, 'stderr':stderr}
def fail_with_message(self, msg, stdout, stderr, writerThread):
raise AssertionError(msg+
"\nStdout: \n"+'\n'.join(stdout)+
"\nStderr:"+'\n'.join(stderr)+
"\nLog:\n"+'\n'.join(getattr(writerThread, 'log', [])))
"\n\n===========================\nStdout: \n"+''.join(stdout)+
"\n\n===========================\nStderr:"+''.join(stderr)+
"\n\n===========================\nLog:\n"+'\n'.join(getattr(writerThread, 'log', [])))
@@ -268,6 +298,7 @@ class DebuggerRunner(object):
class AbstractWriterThread(threading.Thread):
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = False
IS_MODULE = False
def __init__(self):
threading.Thread.__init__(self)
@@ -276,6 +307,9 @@ class AbstractWriterThread(threading.Thread):
self._next_breakpoint_id = 0
self.log = []
def additional_output_checks(self, stdout, stderr):
pass
def get_environ(self):
return None
@@ -291,12 +325,17 @@ class AbstractWriterThread(threading.Thread):
return [self.TEST_FILE]
def do_kill(self):
if hasattr(self, 'server_socket'):
self.server_socket.close()
if hasattr(self, 'reader_thread'):
# if it's not created, it's not there...
self.reader_thread.do_kill()
self.sock.close()
if hasattr(self, 'sock'):
self.sock.close()
def write(self, s):
self.log.append('write: %s' % (s,))
last = self.reader_thread.last_received
if SHOW_WRITES_AND_READS:
@@ -313,16 +352,17 @@ class AbstractWriterThread(threading.Thread):
time.sleep(0.1)
def start_socket(self):
def start_socket(self, port=0):
if SHOW_WRITES_AND_READS:
print('start_socket')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 0))
s.bind(('', port))
self.port = s.getsockname()[1]
s.listen(1)
if SHOW_WRITES_AND_READS:
print('Waiting in socket.accept()')
self.server_socket = s
newSock, addr = s.accept()
if SHOW_WRITES_AND_READS:
print('Test Writer Thread Socket:', newSock, addr)
@@ -359,7 +399,7 @@ class AbstractWriterThread(threading.Thread):
thread_id = splitted[3]
return thread_id
def wait_for_breakpoint_hit(self, reason='111', get_line=False):
def wait_for_breakpoint_hit(self, reason='111', get_line=False, get_name=False):
'''
108 is over
109 is return
@@ -375,22 +415,29 @@ class AbstractWriterThread(threading.Thread):
last = self.reader_thread.last_received
if i >= 10:
raise AssertionError('After %s seconds, a break with reason: %s was not hit. Found: %s' % \
(i, reason, last))
(i, reason, last))
# we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" ...
# we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" name="encode" ...
splitted = last.split('"')
thread_id = splitted[1]
frameId = splitted[7]
name = splitted[9]
if get_line:
self.log.append('End(0): wait_for_breakpoint_hit')
self.log.append('End(0): wait_for_breakpoint_hit: %s' % (last,))
try:
return thread_id, frameId, int(splitted[13])
if not get_name:
return thread_id, frameId, int(splitted[13])
else:
return thread_id, frameId, int(splitted[13]), name
except:
raise AssertionError('Error with: %s, %s, %s.\nLast: %s.\n\nAll: %s\n\nSplitted: %s' % (
thread_id, frameId, splitted[13], last, '\n'.join(self.reader_thread.all_received), splitted))
self.log.append('End(1): wait_for_breakpoint_hit')
return thread_id, frameId
self.log.append('End(1): wait_for_breakpoint_hit: %s' % (last,))
if not get_name:
return thread_id, frameId
else:
return thread_id, frameId, name
def wait_for_custom_operation(self, expected):
i = 0
@@ -401,7 +448,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the custom operation not received. Last found:\n%s\nExpected (encoded)\n%s' %
(i, self.reader_thread.last_received, expectedEncoded))
(i, self.reader_thread.last_received, expectedEncoded))
return True
@@ -417,7 +464,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' %
(i, self.reader_thread.last_received))
(i, self.reader_thread.last_received))
return True
@@ -461,7 +508,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, %s. Last found:\n%s' %
(i, error_msg, last))
(i, error_msg, last))
return True
@@ -479,7 +526,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' %
(i, self.reader_thread.last_received))
(i, self.reader_thread.last_received))
return True
@@ -490,12 +537,15 @@ class AbstractWriterThread(threading.Thread):
def write_version(self):
self.write("501\t%s\t1.0\tWINDOWS\tID" % self.next_seq())
def get_main_filename(self):
return self.TEST_FILE
def write_add_breakpoint(self, line, func):
'''
@param line: starts at 1
'''
breakpoint_id = self.next_breakpoint_id()
self.write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.next_seq(), breakpoint_id, 'python-line', self.TEST_FILE, line, func))
self.write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.next_seq(), breakpoint_id, 'python-line', self.get_main_filename(), line, func))
self.log.append('write_add_breakpoint: %s line: %s func: %s' % (breakpoint_id, line, func))
return breakpoint_id
@@ -504,7 +554,7 @@ class AbstractWriterThread(threading.Thread):
self.log.append('write_add_exception_breakpoint: %s' % (exception,))
def write_remove_breakpoint(self, breakpoint_id):
self.write("112\t%s\t%s\t%s\t%s" % (self.next_seq(), 'python-line', self.TEST_FILE, breakpoint_id))
self.write("112\t%s\t%s\t%s\t%s" % (self.next_seq(), 'python-line', self.get_main_filename(), breakpoint_id))
def write_change_variable(self, thread_id, frame_id, varname, value):
self.write("117\t%s\t%s\t%s\t%s\t%s\t%s" % (self.next_seq(), thread_id, frame_id, 'FRAME', varname, value))
@@ -5,7 +5,7 @@
Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
'''
from tests_python.debugger_unittest import get_free_port
from tests_pydevd_python.debugger_unittest import get_free_port
import threading
@@ -17,12 +17,16 @@ IRONPYTHON_EXE = None
JYTHON_JAR_LOCATION = None
JAVA_LOCATION = None
try:
xrange
except:
xrange = range
import unittest
import os
import sys
import time
from tests_python import debugger_unittest
from tests_pydevd_python import debugger_unittest
TEST_DJANGO = False
if sys.version_info[:2] == (2, 7):
@@ -239,10 +243,13 @@ class WriterThreadCase17a(debugger_unittest.AbstractWriterThread):
self.write_make_initial_run()
thread_id, frame_id, line = self.wait_for_breakpoint_hit('111', True)
assert line == 2, 'Expected return to be in line 2, was: %s' % line
self.write_step_in(thread_id)
thread_id, frame_id, line = self.wait_for_breakpoint_hit('107', True)
thread_id, frame_id, line, name = self.wait_for_breakpoint_hit('107', get_line=True, get_name=True)
# Should Skip step into properties setter
assert name == 'm3'
assert line == 10, 'Expected return to be in line 10, was: %s' % line
self.write_run_thread(thread_id)
@@ -284,15 +291,17 @@ class WriterThreadCase16(debugger_unittest.AbstractWriterThread):
self.wait_for_var('<var name="%27size%27')
self.write_get_variable(thread_id, frame_id, 'bigarray')
# isContainer could be true on some numpy versions, so, we only check for the var begin.
self.wait_for_var([
'<var name="min" type="int64" qualifier="numpy" value="int64%253A 0" />',
'<var name="min" type="int64" qualifier="numpy" value="int64%3A 0" />',
'<var name="size" type="int" qualifier="{}" value="int%3A 100000" />'.format(builtin_qualifier),
'<var name="min" type="int64" qualifier="numpy" value="int64%253A 0"',
'<var name="min" type="int64" qualifier="numpy" value="int64%3A 0"',
'<var name="size" type="int" qualifier="{}" value="int%3A 100000"'.format(builtin_qualifier),
])
self.wait_for_var([
'<var name="max" type="int64" qualifier="numpy" value="int64%253A 99999" />',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999" />',
'<var name="max" type="int64" qualifier="numpy" value="int64%3A 99999"'
'<var name="max" type="int64" qualifier="numpy" value="int64%253A 99999"',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999"',
'<var name="max" type="int64" qualifier="numpy" value="int64%3A 99999"',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999"',
])
self.wait_for_var('<var name="shape" type="tuple"')
self.wait_for_var('<var name="dtype" type="dtype"')
@@ -306,10 +315,14 @@ class WriterThreadCase16(debugger_unittest.AbstractWriterThread):
self.wait_for_var([
'<var name="min" type="str" qualifier={} value="str%253A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier={} value="str%3A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier="{}" value="str%253A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier="{}" value="str%3A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
])
self.wait_for_var([
'<var name="max" type="str" qualifier={} value="str%253A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier={} value="str%3A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier="{}" value="str%253A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier="{}" value="str%3A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
])
self.wait_for_var('<var name="shape" type="tuple"')
self.wait_for_var('<var name="dtype" type="dtype"')
@@ -487,7 +500,9 @@ class WriterThreadCase11(debugger_unittest.AbstractWriterThread):
self.write_add_breakpoint(2, 'Method1')
self.write_make_initial_run()
thread_id, frame_id = self.wait_for_breakpoint_hit('111')
thread_id, frame_id, line = self.wait_for_breakpoint_hit('111', True)
assert line == 2, 'Expected return to be in line 2, was: %s' % line
self.write_step_over(thread_id)
@@ -877,6 +892,37 @@ class WriterThreadCaseQThread3(debugger_unittest.AbstractWriterThread):
self.log.append('Marking finished ok.')
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseQThread4
#=======================================================================================================================
class WriterThreadCaseQThread4(debugger_unittest.AbstractWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_qthread4.py')
def run(self):
self.start_socket()
breakpoint_id = self.write_add_breakpoint(24, 'on_start')
self.write_make_initial_run()
thread_id, frame_id = self.wait_for_breakpoint_hit()
self.write_remove_breakpoint(breakpoint_id)
self.write_run_thread(thread_id)
self.log.append('Checking sequence. Found: %s' % (self._sequence))
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
self.log.append('Marking finished ok.')
self.finished_ok = True
def additional_output_checks(self, stdout, stderr):
if 'On start called' not in stdout:
raise AssertionError('Expected "On start called" to be in stdout:\n%s' % (stdout,))
if 'Done sleeping' not in stdout:
raise AssertionError('Expected "Done sleeping" to be in stdout:\n%s' % (stdout,))
if 'native Qt signal is not callable' in stderr:
raise AssertionError('Did not expect "native Qt signal is not callable" to be in stderr:\n%s' % (stderr,))
#=======================================================================================================================
# WriterThreadCase1
#=======================================================================================================================
@@ -918,6 +964,162 @@ class WriterThreadCase1(debugger_unittest.AbstractWriterThread):
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseMSwitch
#=======================================================================================================================
class WriterThreadCaseMSwitch(debugger_unittest.AbstractWriterThread):
TEST_FILE = 'tests_python._debugger_case_m_switch'
IS_MODULE = True
def get_environ(self):
env = os.environ.copy()
curr_pythonpath = env.get('PYTHONPATH', '')
root_dirname = os.path.dirname(os.path.dirname(__file__))
curr_pythonpath += root_dirname + os.pathsep
env['PYTHONPATH'] = curr_pythonpath
return env
def get_main_filename(self):
return debugger_unittest._get_debugger_test_file('_debugger_case_m_switch.py')
def run(self):
self.start_socket()
self.log.append('writing add breakpoint')
breakpoint_id = self.write_add_breakpoint(1, None)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit()
self.write_remove_breakpoint(breakpoint_id)
self.log.append('run thread')
self.write_run_thread(thread_id)
self.log.append('asserting')
try:
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseRemoteDebugger
#=======================================================================================================================
class WriterThreadCaseRemoteDebugger(debugger_unittest.AbstractWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_remote.py')
def run(self):
self.start_socket(8787)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit('105')
self.log.append('run thread')
self.write_run_thread(thread_id)
self.log.append('asserting')
try:
assert 5 == self._sequence, 'Expected 5. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
#=======================================================================================================================
# _SecondaryMultiProcProcessWriterThread
#=======================================================================================================================
class _SecondaryMultiProcProcessWriterThread(debugger_unittest.AbstractWriterThread):
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = True
def __init__(self, server_socket):
debugger_unittest.AbstractWriterThread.__init__(self)
self.server_socket = server_socket
def run(self):
print('waiting for second process')
self.sock, addr = self.server_socket.accept()
print('accepted second process')
from tests_python.debugger_unittest import ReaderThread
self.reader_thread = ReaderThread(self.sock)
self.reader_thread.start()
self._sequence = -1
# initial command is always the version
self.write_version()
self.log.append('start_socket')
self.write_make_initial_run()
time.sleep(.5)
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseRemoteDebuggerMultiProc
#=======================================================================================================================
class WriterThreadCaseRemoteDebuggerMultiProc(debugger_unittest.AbstractWriterThread):
# It seems sometimes it becomes flaky on the ci because the process outlives the writer thread...
# As we're only interested in knowing if a second connection was received, just kill the related
# process.
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = True
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_remote_1.py')
def run(self):
self.start_socket(8787)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit('105')
self.secondary_multi_proc_process_writer_thread = secondary_multi_proc_process_writer_thread = \
_SecondaryMultiProcProcessWriterThread(self.server_socket)
secondary_multi_proc_process_writer_thread.start()
self.log.append('run thread')
self.write_run_thread(thread_id)
for _i in xrange(400):
if secondary_multi_proc_process_writer_thread.finished_ok:
break
time.sleep(.1)
else:
self.log.append('Secondary process not finished ok!')
raise AssertionError('Secondary process not finished ok!')
self.log.append('Secondary process finished!')
try:
assert 5 == self._sequence, 'Expected 5. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
def do_kill(self):
debugger_unittest.AbstractWriterThread.do_kill(self)
if hasattr(self, 'secondary_multi_proc_process_writer_thread'):
self.secondary_multi_proc_process_writer_thread.do_kill()
#=======================================================================================================================
# DebuggerBase
#=======================================================================================================================
@@ -1016,6 +1218,29 @@ class DebuggerBase(debugger_unittest.DebuggerRunner):
if self._has_qt():
self.check_case(WriterThreadCaseQThread3)
def test_case_qthread4(self):
if self._has_qt():
self.check_case(WriterThreadCaseQThread4)
def test_m_switch(self):
self.check_case(WriterThreadCaseMSwitch)
class TestPythonRemoteDebugger(unittest.TestCase, debugger_unittest.DebuggerRunner):
def get_command_line(self):
return [PYTHON_EXE, '-u']
def add_command_line_args(self, args):
return args + [self.writer_thread.TEST_FILE]
def test_remote_debugger(self):
self.check_case(WriterThreadCaseRemoteDebugger)
def test_remote_debugger2(self):
self.check_case(WriterThreadCaseRemoteDebuggerMultiProc)
class TestPython(unittest.TestCase, DebuggerBase):
def get_command_line(self):
@@ -1154,7 +1379,7 @@ if __name__ == '__main__':
#
# suite.addTests(unittest.makeSuite(TestIronPython))
#
suite.addTests(unittest.makeSuite(TestPython))
# suite.addTests(unittest.makeSuite(TestPython))
@@ -1178,6 +1403,6 @@ if __name__ == '__main__':
# unittest.TextTestRunner(verbosity=3).run(suite)
# suite.addTest(TestPython('test_case_17'))
# suite.addTest(TestPython('test_case_18'))
# suite.addTest(TestPython('test_case_19'))
suite.addTest(TestPython('test_case_qthread4'))
unittest.TextTestRunner(verbosity=3).run(suite)
@@ -0,0 +1,28 @@
import unittest
class Test(unittest.TestCase):
def testProcessCommandLine(self):
from _pydevd_bundle.pydevd_command_line_handling import process_command_line, setup_to_argv
setup = process_command_line(['pydevd.py', '--port', '1', '--save-threading'])
assert setup['save-threading']
assert setup['port'] == 1
assert not setup['qt-support']
argv = setup_to_argv(setup)
assert argv[0].endswith('pydevd.py')
argv = argv[1:]
assert argv == ['--port', '1', '--save-threading']
def testProcessCommandLine2(self):
from _pydevd_bundle.pydevd_command_line_handling import process_command_line, setup_to_argv
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support=auto'])
assert setup['qt-support'] == 'auto'
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support'])
assert setup['qt-support'] == 'auto'
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support=pyqt4'])
assert setup['qt-support'] == 'pyqt4'
self.assertRaises(ValueError, process_command_line, ['pydevd.py', '--port', '1', '--qt-support=wrong'])
@@ -19,14 +19,13 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check='''C:\\bin\\python.exe -u -c connect(\\"127.0.0.1\\")'''
sys.original_argv = []
debug_command = (
'import sys; '
'sys.path.append(r\'%s\'); '
'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, '
'trace_only_current_thread=False, patch_multiprocessing=True); '
'sys.original_argv = []; '
'connect("127.0.0.1")') % pydev_src_dir
'import sys; '
'sys.path.append(r\'%s\'); '
'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, '
'trace_only_current_thread=False, patch_multiprocessing=True); '
''
'connect("127.0.0.1")') % pydev_src_dir
if sys.platform == "win32":
debug_command = debug_command.replace('"', '\\"')
debug_command = '"%s"' % debug_command
@@ -45,12 +44,11 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', '-u', '-c', 'connect("127.0.0.1")']
sys.original_argv = []
debug_command = (
'import sys; sys.path.append(r\'%s\'); import pydevd; '
'pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); '
'sys.original_argv = []; '
'connect("127.0.0.1")') % pydev_src_dir
'import sys; sys.path.append(r\'%s\'); import pydevd; '
'pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); '
''
'connect("127.0.0.1")') % pydev_src_dir
if sys.platform == "win32":
debug_command = debug_command.replace('"', '\\"')
debug_command = '"%s"' % debug_command
@@ -68,14 +66,19 @@ class TestCase(unittest.TestCase):
original = SetupHolder.setup
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0', 'multiprocess': True}
check=['C:\\bin\\python.exe', '-m', 'test']
sys.original_argv = ['pydevd', '--multiprocess']
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe',
'pydevd',
get_pydevd_file(),
'--module',
'--port',
'0',
'--client',
'127.0.0.1',
'--multiprocess',
'--file',
'test',
])
finally:
@@ -87,9 +90,16 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'connect(\\"127.0.0.1\\")']
sys.original_argv = ['my', 'original', 'argv']
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe', 'my', 'original', 'argv', 'connect(\\"127.0.0.1\\")'])
'C:\\bin\\python.exe',
get_pydevd_file(),
'--port',
'0',
'--client',
'127.0.0.1',
'--file',
'connect(\\"127.0.0.1\\")'])
finally:
SetupHolder.setup = original
@@ -99,7 +109,6 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'pydevd.py', 'connect(\\"127.0.0.1\\")', 'bar']
sys.original_argv = ['my', 'original', 'argv']
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe', 'pydevd.py', 'connect(\\"127.0.0.1\\")', 'bar'])
@@ -108,18 +117,19 @@ class TestCase(unittest.TestCase):
def test_monkey_patch_args_no_indc_without_pydevd(self):
original = SetupHolder.setup
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'target.py', 'connect(\\"127.0.0.1\\")', 'bar']
sys.original_argv = ['pydevd.py', '--a=1', 'b', '--c=2', '--file', 'ignore_this.py']
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe',
'pydevd.py',
'--a=1',
'b',
'--c=2',
get_pydevd_file(),
'--port',
'0',
'--client',
'127.0.0.1',
'--file',
'target.py',
'connect(\\"127.0.0.1\\")',