diff --git a/python/helpers/pydev/_pydev_bundle/pydev_monkey.py b/python/helpers/pydev/_pydev_bundle/pydev_monkey.py index 145dfdf53127..2870671164cf 100644 --- a/python/helpers/pydev/_pydev_bundle/pydev_monkey.py +++ b/python/helpers/pydev/_pydev_bundle/pydev_monkey.py @@ -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 ...) diff --git a/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_pytest2.py b/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_pytest2.py index 61f264d10e62..68b1f4a28acf 100644 --- a/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_pytest2.py +++ b/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_pytest2.py @@ -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', '', '', '', 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) diff --git a/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_unittest.py b/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_unittest.py index 2922f250688d..f0ad9efdbb8f 100644 --- a/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_unittest.py +++ b/python/helpers/pydev/_pydev_runfiles/pydev_runfiles_unittest.py @@ -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) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_command_line_handling.py b/python/helpers/pydev/_pydevd_bundle/pydevd_command_line_handling.py new file mode 100644 index 000000000000..c106a5133e05 --- /dev/null +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_command_line_handling.py @@ -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=, 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 diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_dont_trace_files.py b/python/helpers/pydev/_pydevd_bundle/pydevd_dont_trace_files.py index aae20cac8b4b..9d3d7366c560 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_dont_trace_files.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_dont_trace_files.py @@ -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, diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 7accaabb39d7..465f6fea9e53 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -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: diff --git a/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py b/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py index d8dca957797c..b99048f0e98e 100644 --- a/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py +++ b/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py @@ -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,)) diff --git a/python/helpers/pydev/setup.py b/python/helpers/pydev/setup.py index 88721ddb2b4e..21a5a27ebd90 100644 --- a/python/helpers/pydev/setup.py +++ b/python/helpers/pydev/setup.py @@ -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 diff --git a/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch.py b/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch.py new file mode 100644 index 000000000000..a5b3706a42ec --- /dev/null +++ b/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch.py @@ -0,0 +1,3 @@ +from . import _debugger_case_m_switch_2 +print(_debugger_case_m_switch_2.ClassToBeImported) +print('TEST SUCEEDED!') \ No newline at end of file diff --git a/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch_2.py b/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch_2.py new file mode 100644 index 000000000000..99c9f4cd08c5 --- /dev/null +++ b/python/helpers/pydev/tests_pydevd_python/_debugger_case_m_switch_2.py @@ -0,0 +1,2 @@ +class ClassToBeImported(object): + pass \ No newline at end of file diff --git a/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote.py b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote.py new file mode 100644 index 000000000000..dd35330bea69 --- /dev/null +++ b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote.py @@ -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!') + \ No newline at end of file diff --git a/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_1.py b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_1.py new file mode 100644 index 000000000000..c69daa2e4ac5 --- /dev/null +++ b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_1.py @@ -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() diff --git a/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_2.py b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_2.py new file mode 100644 index 000000000000..d25bb771d4ab --- /dev/null +++ b/python/helpers/pydev/tests_pydevd_python/_debugger_case_remote_2.py @@ -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() \ No newline at end of file diff --git a/python/helpers/pydev/tests_pydevd_python/debugger_unittest.py b/python/helpers/pydev/tests_pydevd_python/debugger_unittest.py index eb20cccf30ce..9e1c42ea0929 100644 --- a/python/helpers/pydev/tests_pydevd_python/debugger_unittest.py +++ b/python/helpers/pydev/tests_pydevd_python/debugger_unittest.py @@ -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 = 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)) diff --git a/python/helpers/pydev/tests_pydevd_python/test_debugger.py b/python/helpers/pydev/tests_pydevd_python/test_debugger.py index 77e4ccbedbcd..00a64774bc7b 100644 --- a/python/helpers/pydev/tests_pydevd_python/test_debugger.py +++ b/python/helpers/pydev/tests_pydevd_python/test_debugger.py @@ -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('', - '', - ''.format(builtin_qualifier), + '', - '', - ''.format(builtin_qualifier), ''.format(builtin_qualifier), + ''.format(builtin_qualifier), + ''.format(builtin_qualifier), ]) self.wait_for_var([ ''.format(builtin_qualifier), ''.format(builtin_qualifier), + ''.format(builtin_qualifier), + ''.format(builtin_qualifier), ]) self.wait_for_var('