mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Updates from PyDev.Debugger: 1.0.0 release (PY-22904)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,))
|
||||
|
||||
@@ -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\\")',
|
||||
|
||||
Reference in New Issue
Block a user