diff --git a/ptvsd.code-workspace b/ptvsd.code-workspace index 8fe2ebf9..367f9036 100644 --- a/ptvsd.code-workspace +++ b/ptvsd.code-workspace @@ -7,6 +7,7 @@ "settings": { "python.linting.enabled": true, "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, "python.envFile": "${workspaceFolder}/.env", "python.formatting.provider": "black", @@ -15,6 +16,7 @@ "**/__pycache__": true, ".tox": true, "src/ptvsd.egg-info": true, - } + }, + "python.testing.pytestEnabled": true, }, } diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_api.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_api.py index 07d0455e..c2d8186a 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_api.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_api.py @@ -759,10 +759,7 @@ class PyDevdAPI(object): reset_caches = True def custom_dont_trace_external_files(abs_path): - for p in start_patterns: - if p in abs_path: - return True - return abs_path.endswith(end_patterns) + return abs_path.startswith(start_patterns) or abs_path.endswith(end_patterns) custom_dont_trace_external_files.start_patterns = start_patterns custom_dont_trace_external_files.end_patterns = end_patterns diff --git a/src/ptvsd/common/log.py b/src/ptvsd/common/log.py index 99ce447d..c906c1ba 100644 --- a/src/ptvsd/common/log.py +++ b/src/ptvsd/common/log.py @@ -14,7 +14,7 @@ import threading import traceback import ptvsd -from ptvsd.common import compat, fmt, options, timestamp +from ptvsd.common import compat, fmt, options, timestamp, util LEVELS = ("debug", "info", "warning", "error") @@ -223,7 +223,7 @@ def describe_environment(header): expr = None if not callable(get_paths): expr = get_paths - get_paths = lambda: eval(expr, {}, sys.modules) + get_paths = lambda: util.evaluate(expr) try: paths = get_paths() except AttributeError: diff --git a/src/ptvsd/common/util.py b/src/ptvsd/common/util.py index 4ba11cc4..2f0bae3d 100644 --- a/src/ptvsd/common/util.py +++ b/src/ptvsd/common/util.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, print_function, unicode_literals import threading +import sys def new_hidden_thread(name, target, prefix='ptvsd.common.', daemon=True, **kwargs): @@ -21,3 +22,12 @@ def new_hidden_thread(name, target, prefix='ptvsd.common.', daemon=True, **kwarg if daemon: t.daemon = False return t + + +def evaluate(code, path=__file__, mode="eval"): + # Setting file path here to avoid breaking here if users have set + # "break on exception raised" setting. This code can potentially run + # in user process and is indistinguishable if the path is not set. + # We use the path internally to skip exception inside the debugger. + expr = compile(code, path, "eval") + return eval(expr, {}, sys.modules) diff --git a/src/ptvsd/server/api.py b/src/ptvsd/server/api.py index 2418c5d0..c8cb2c37 100644 --- a/src/ptvsd/server/api.py +++ b/src/ptvsd/server/api.py @@ -13,10 +13,7 @@ import ptvsd from ptvsd.common import log, options as common_opts from ptvsd.server import options as server_opts from _pydevd_bundle.pydevd_constants import get_global_debugger -from pydevd_file_utils import ( - get_abs_path_real_path_and_base_from_file, - get_abs_path_real_path_and_base_from_frame, -) +from pydevd_file_utils import get_abs_path_real_path_and_base_from_file def wait_for_attach(): @@ -107,9 +104,7 @@ def break_into_debugger(): stop_at_frame = sys._getframe().f_back while ( stop_at_frame is not None - and global_debugger.get_file_type( - stop_at_frame, get_abs_path_real_path_and_base_from_frame(stop_at_frame) - ) + and global_debugger.get_file_type(stop_at_frame) == global_debugger.PYDEV_FILE ): stop_at_frame = stop_at_frame.f_back diff --git a/tests/debug.py b/tests/debug.py index 3c860021..cf04fc10 100644 --- a/tests/debug.py +++ b/tests/debug.py @@ -27,8 +27,7 @@ StopInfo = collections.namedtuple( PTVSD_DIR = py.path.local(ptvsd.__file__) / ".." PTVSD_ADAPTER_DIR = PTVSD_DIR / "adapter" -# Added to the environment variables of every new debug.Session - after copying -# os.environ(), but before setting any session-specific variables. +# Added to the environment variables of every new debug.Session PTVSD_ENV = {"PYTHONUNBUFFERED": "1"} @@ -120,6 +119,11 @@ class Session(object): # Log the error, in case another one happens during shutdown. log.exception(exc_info=(exc_type, exc_val, exc_tb)) + try: + self.wait_for_exit() + except Exception: + raise log.exception() + self._stop_adapter() if self.backchannel: @@ -128,12 +132,24 @@ class Session(object): @property def process(self): - return self.start_method.debugee_process + return self.start_method.debuggee_process + + @property + def pid(self): + return self.process.pid @property def ignore_unobserved(self): return self.timeline.ignore_unobserved + @property + def expected_exit_code(self): + return self.start_method.expected_exit_code + + @expected_exit_code.setter + def expected_exit_code(self, value): + self.start_method.expected_exit_code = value + def request(self, *args, **kwargs): freeze = kwargs.pop("freeze", True) raise_if_failed = kwargs.pop("raise_if_failed", True) @@ -257,6 +273,7 @@ class Session(object): if pythonpath: pythonpath += os.pathsep pythonpath += (tests.root / "DEBUGGEE_PYTHONPATH").strpath + pythonpath += os.pathsep + (PTVSD_DIR / "..").strpath env["PYTHONPATH"] = pythonpath env["PTVSD_SESSION_ID"] = str(self.id) @@ -395,6 +412,7 @@ class Session(object): "exception", "breakpoint", "entry", + "goto" ]: expected_stopped["preserveFocusHint"] = True assert stopped == some.dict.containing(expected_stopped) @@ -431,6 +449,6 @@ class Session(object): def captured_stderr(self, encoding=None): return self.start_method.captured_output.stderr(encoding) - def stop_debugging(self, exit_code=None): - self.start_method.wait_for_debuggee(exit_code) + def wait_for_exit(self): + self.start_method.wait_for_debuggee() self.request_disconnect() diff --git a/tests/ptvsd/server/test_args.py b/tests/ptvsd/server/test_args.py index 49f7126d..b0fc5b5f 100644 --- a/tests/ptvsd/server/test_args.py +++ b/tests/ptvsd/server/test_args.py @@ -28,5 +28,3 @@ def test_args(pyfile, start_method, run_as): argv = session.backchannel.receive() assert argv == [some.str] + expected - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_attach.py b/tests/ptvsd/server/test_attach.py index ca554bce..6e5e4fd9 100644 --- a/tests/ptvsd/server/test_attach.py +++ b/tests/ptvsd/server/test_attach.py @@ -53,7 +53,6 @@ def test_attach(run_as, wait_for_attach, is_attached, stop_method): pytest.fail(stop_method) session.request_continue() - session.stop_debugging() @pytest.mark.parametrize( @@ -129,5 +128,3 @@ def test_attaching_by_pid(pyfile, run_as, start_method): session.wait_for_next( Event("output", some.dict.containing({"category": "stdout"})) ) - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_break_into_dbg.py b/tests/ptvsd/server/test_break_into_dbg.py index 561fb1ba..f953e1bd 100644 --- a/tests/ptvsd/server/test_break_into_dbg.py +++ b/tests/ptvsd/server/test_break_into_dbg.py @@ -27,7 +27,6 @@ def test_with_wait_for_attach(pyfile, start_method, run_as): assert hit.frames[0]["line"] == code_to_debug.lines["break"] session.request_continue() - session.stop_debugging() @pytest.mark.parametrize("run_as", ["program", "module", "code"]) @@ -52,4 +51,3 @@ def test_breakpoint_function(pyfile, start_method, run_as): assert hit.frames[0]["line"] == code_to_debug.lines["break"] session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_breakpoints.py b/tests/ptvsd/server/test_breakpoints.py index 02e540f0..de3d957c 100644 --- a/tests/ptvsd/server/test_breakpoints.py +++ b/tests/ptvsd/server/test_breakpoints.py @@ -34,7 +34,6 @@ def test_path_with_ampersand(start_method, run_as): ) session.request_continue() - session.stop_debugging() @pytest.mark.skipif( @@ -58,7 +57,6 @@ def test_path_with_unicode(start_method, run_as): ) session.request_continue() - session.stop_debugging() @pytest.mark.parametrize( @@ -122,7 +120,6 @@ def test_conditional_breakpoint(pyfile, start_method, run_as, condition_kind): for i in range(1, hits): session.wait_for_stop() session.request_continue() - session.stop_debugging() def test_crossfile_breakpoint(pyfile, start_method, run_as): @@ -154,7 +151,6 @@ def test_crossfile_breakpoint(pyfile, start_method, run_as): session.wait_for_stop(expected_frames=[some.dap.frame(script1, line="bp")]) session.request_continue() - session.stop_debugging() @pytest.mark.parametrize("error_name", ["NameError", ""]) @@ -188,15 +184,14 @@ def test_error_in_condition(pyfile, start_method, run_as, error_name): }, ) session.start_debugging() - session.stop_debugging() - assert not session.captured_stdout() + assert not session.captured_stdout() - error_name = error_name.encode("ascii") - if expect_traceback: - assert error_name in session.captured_stderr() - else: - assert error_name not in session.captured_stderr() + error_name = error_name.encode("ascii") + if expect_traceback: + assert error_name in session.captured_stderr() + else: + assert error_name not in session.captured_stderr() @pytest.mark.parametrize("condition", ["condition", ""]) @@ -248,17 +243,16 @@ def test_log_point(pyfile, start_method, run_as, condition): ], ) session.request_continue() - session.stop_debugging() - assert not session.captured_stderr() + assert not session.captured_stderr() - expected_stdout = "".join( - ( - fmt(r"{0}\r?\n{1}\r?\n", re.escape(str(i)), re.escape(str(i * 10))) - for i in range(0, 10) - ) + expected_stdout = "".join( + ( + fmt(r"{0}\r?\n{1}\r?\n", re.escape(str(i)), re.escape(str(i * 10))) + for i in range(0, 10) ) - assert session.output("stdout") == some.str.matching(expected_stdout) + ) + assert session.output("stdout") == some.str.matching(expected_stdout) def test_package_launch(): @@ -266,6 +260,7 @@ def test_package_launch(): test_py = cwd / "pkg1" / "__main__.py" with debug.Session(start_methods.Launch) as session: + session.expected_exit_code = 42 session.configure("module", "pkg1", cwd=cwd) session.set_breakpoints(test_py, ["two"]) session.start_debugging() @@ -278,7 +273,6 @@ def test_package_launch(): ) session.request_continue() - session.stop_debugging(exitCode=42) def test_add_and_remove_breakpoint(pyfile, start_method, run_as): @@ -313,10 +307,9 @@ def test_add_and_remove_breakpoint(pyfile, start_method, run_as): ], ) session.request_continue() - session.stop_debugging() - expected_stdout = "".join((fmt("{0}\n", i) for i in range(0, 10))) - assert session.output("stdout") == expected_stdout + expected_stdout = "".join((fmt("{0}\n", i) for i in range(0, 10))) + assert session.output("stdout") == expected_stdout def test_invalid_breakpoints(pyfile, start_method, run_as): @@ -375,8 +368,6 @@ def test_invalid_breakpoints(pyfile, start_method, run_as): ) session.request_continue() - session.stop_debugging() - def test_deep_stacks(pyfile, start_method, run_as): @pyfile @@ -417,4 +408,3 @@ def test_deep_stacks(pyfile, start_method, run_as): assert stop.frames == frames session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_completions.py b/tests/ptvsd/server/test_completions.py index fd8740a7..5bb7730e 100644 --- a/tests/ptvsd/server/test_completions.py +++ b/tests/ptvsd/server/test_completions.py @@ -66,11 +66,9 @@ def test_completions_scope(pyfile, bp_label, start_method, run_as): session.request_continue() - targets.sort(key=lambda t: t["label"]) - expected.sort(key=lambda t: t["label"]) - assert targets == expected - - session.stop_debugging() + targets.sort(key=lambda t: t["label"]) + expected.sort(key=lambda t: t["label"]) + assert targets == expected def test_completions_cases(pyfile, start_method, run_as): @@ -132,4 +130,3 @@ def test_completions_cases(pyfile, start_method, run_as): assert "Wrong ID sent from the client:" in str(error) session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_disconnect.py b/tests/ptvsd/server/test_disconnect.py index 4c241496..c61a2046 100644 --- a/tests/ptvsd/server/test_disconnect.py +++ b/tests/ptvsd/server/test_disconnect.py @@ -29,12 +29,10 @@ def test_continue_on_disconnect_for_attach(pyfile, start_method, run_as): hit = session.wait_for_stop("breakpoint") assert hit.frames[0]["line"] == code_to_debug.lines["bp"] session.send_request("disconnect").wait_for_response() - session.wait_for_disconnect() assert "continued" == backchannel.receive() @pytest.mark.parametrize("start_method", [start_methods.Launch]) -@pytest.mark.skip(reason="Bug #1052") def test_exit_on_disconnect_for_launch(pyfile, start_method, run_as): @pyfile def code_to_debug(): @@ -47,12 +45,13 @@ def test_exit_on_disconnect_for_launch(pyfile, start_method, run_as): print("Should not continue after disconnect on launch", file=f) with debug.Session(start_method) as session: + session.expected_exit_code = some.int session.configure(run_as, code_to_debug) session.set_breakpoints(code_to_debug, code_to_debug.lines["bp"]) session.start_debugging() hit = session.wait_for_stop("breakpoint") assert hit.frames[0]["line"] == code_to_debug.lines["bp"] session.send_request("disconnect").wait_for_response() - session.stop_debugging(exitCode=some.int) - fp = os.join(os.path.dirname(os.path.abspath(code_to_debug)), "here.txt") - assert not os.path.exists(fp) + + fp = os.join(os.path.dirname(os.path.abspath(code_to_debug)), "here.txt") + assert not os.path.exists(fp) diff --git a/tests/ptvsd/server/test_django.py b/tests/ptvsd/server/test_django.py index db1f9035..81cd862c 100644 --- a/tests/ptvsd/server/test_django.py +++ b/tests/ptvsd/server/test_django.py @@ -25,18 +25,19 @@ class lines: app_py = code.get_marked_line_numbers(paths.app_py) -def _initialize_session(session, multiprocess=False): - session.program_args = ["runserver", "--", str(django.port)] +def _initialize_session(session, multiprocess=False, exit_code=0): + args = ["runserver"] if not multiprocess: - session.program_args[1:1] = ["--noreload"] - - session.debug_options |= {"Django"} - if multiprocess: - session.debug_options |= {"Multiprocess"} + args += ["--noreload"] + args += ["--", str(django.port)] + session.expected_exit_code = exit_code session.configure( "program", paths.app_py, cwd=paths.django1, + multiprocess=multiprocess, + args=args, + django=True ) @@ -50,7 +51,7 @@ def test_django_breakpoint_no_multiproc(start_method, bp_target): bp_var_content = compat.force_str("Django-Django-Test") with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() @@ -82,15 +83,11 @@ def test_django_breakpoint_no_multiproc(start_method, bp_target): session.request_continue() assert bp_var_content in home_request.response_text() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Django server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch, start_methods.AttachSocketCmdLine]) def test_django_template_exception_no_multiproc(start_method): with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) session.request("setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}) session.start_debugging() @@ -131,10 +128,6 @@ def test_django_template_exception_no_multiproc(start_method): session.wait_for_stop("exception") session.request_continue() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Django server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch, start_methods.AttachSocketCmdLine]) @pytest.mark.parametrize("exc_type", ["handled", "unhandled"]) @@ -142,7 +135,7 @@ def test_django_exception_no_multiproc(start_method, exc_type): exc_line = lines.app_py["exc_" + exc_type] with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) session.request("setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}) session.start_debugging() @@ -185,10 +178,6 @@ def test_django_exception_no_multiproc(start_method, exc_type): session.request_continue() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Django server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch]) def test_django_breakpoint_multiproc(start_method): @@ -196,7 +185,7 @@ def test_django_breakpoint_multiproc(start_method): bp_var_content = compat.force_str("Django-Django-Test") with debug.Session(start_method) as parent_session: - _initialize_session(parent_session, multiprocess=True) + _initialize_session(parent_session, multiprocess=True, exit_code=some.int) parent_session.set_breakpoints(paths.app_py, [bp_line]) parent_session.start_debugging() @@ -228,6 +217,3 @@ def test_django_breakpoint_multiproc(start_method): child_session.request_continue() assert bp_var_content in home_request.response_text() - - child_session.stop_debugging() - parent_session.stop_debugging(exitCode=some.int) diff --git a/tests/ptvsd/server/test_evaluate.py b/tests/ptvsd/server/test_evaluate.py index 9892ca6e..1354128f 100644 --- a/tests/ptvsd/server/test_evaluate.py +++ b/tests/ptvsd/server/test_evaluate.py @@ -100,7 +100,6 @@ def test_variables_and_evaluate(pyfile, start_method, run_as): ) session.request_continue() - session.stop_debugging() def test_set_variable(pyfile, start_method, run_as): @@ -156,8 +155,6 @@ def test_set_variable(pyfile, start_method, run_as): assert backchannel.receive() == 1000 - session.stop_debugging() - def test_variable_sort(pyfile, start_method, run_as): @pyfile @@ -245,7 +242,6 @@ def test_variable_sort(pyfile, start_method, run_as): # assert variable_names[:3] == ['1', '2', '10'] session.request_continue() - session.stop_debugging() def test_return_values(pyfile, start_method, run_as): @@ -333,7 +329,6 @@ def test_return_values(pyfile, start_method, run_as): assert variables == [expected1, expected2] session.send_request("continue").wait_for_response() - session.stop_debugging() def test_unicode(pyfile, start_method, run_as): @@ -367,7 +362,6 @@ def test_unicode(pyfile, start_method, run_as): assert resp_eval.body == some.dict.containing({"type": "SyntaxError"}) session.request_continue() - session.stop_debugging() def test_hex_numbers(pyfile, start_method, run_as): @@ -592,4 +586,3 @@ def test_hex_numbers(pyfile, start_method, run_as): ] session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_exception.py b/tests/ptvsd/server/test_exception.py index 183a72f0..f1dc746c 100644 --- a/tests/ptvsd/server/test_exception.py +++ b/tests/ptvsd/server/test_exception.py @@ -36,6 +36,7 @@ def test_vsc_exception_options_raise_with_except( filters += ["uncaught"] if uncaught == "uncaughtOn" else [] with debug.Session(start_method) as session: + session.expected_exit_code = some.int session.configure(run_as, code_to_debug) session.request("setExceptionBreakpoints", {"filters": filters}) session.start_debugging() @@ -72,8 +73,6 @@ def test_vsc_exception_options_raise_with_except( # uncaught should not 'stop' matter since the exception is caught - session.stop_debugging() - @pytest.mark.parametrize("raised", ["raisedOn", "raisedOff"]) @pytest.mark.parametrize("uncaught", ["uncaughtOn", "uncaughtOff"]) @@ -95,6 +94,7 @@ def test_vsc_exception_options_raise_without_except( filters += ["uncaught"] if uncaught == "uncaughtOn" else [] with debug.Session(start_method) as session: session.ignore_unobserved += [Event("stopped")] + session.expected_exit_code = some.int session.configure(run_as, code_to_debug) session.send_request( "setExceptionBreakpoints", {"filters": filters} @@ -160,8 +160,6 @@ def test_vsc_exception_options_raise_without_except( assert resp_exc_info.body == expected session.request_continue() - session.stop_debugging(exitCode=some.int) - @pytest.mark.parametrize("raised", ["raised", ""]) @pytest.mark.parametrize("uncaught", ["uncaught", ""]) @@ -190,10 +188,8 @@ def test_systemexit(pyfile, start_method, run_as, raised, uncaught, zero, exit_c filters += ["uncaught"] with debug.Session(start_method) as session: - session.program_args = [repr(exit_code)] - if zero: - session.debug_options |= {"BreakOnSystemExitZero"} - session.configure(run_as, code_to_debug) + session.expected_exit_code = some.int + session.configure(run_as, code_to_debug, args=[repr(exit_code)], breakOnSystemExitZero=bool(zero)) session.send_request( "setExceptionBreakpoints", {"filters": filters} ).wait_for_response() @@ -221,8 +217,6 @@ def test_systemexit(pyfile, start_method, run_as, raised, uncaught, zero, exit_c assert hit.frames[0]["line"] == line_numbers["unhandled"] session.request_continue() - session.stop_debugging(exitCode=some.int) - @pytest.mark.parametrize( "break_mode", ["always", "never", "unhandled", "userUnhandled"] @@ -281,6 +275,7 @@ def test_raise_exception_options(pyfile, start_method, run_as, exceptions, break with debug.Session(start_method) as session: session.ignore_unobserved += [Event("stopped")] + session.expected_exit_code = some.int session.configure(run_as, code_to_debug) path = [{"names": ["Python Exceptions"]}] if exceptions: @@ -305,8 +300,6 @@ def test_raise_exception_options(pyfile, start_method, run_as, exceptions, break assert hit.frames[0]["line"] == code_to_debug.lines[expected_exception] session.request_continue() - session.stop_debugging(exitCode=some.int) - @pytest.mark.parametrize("exit_code", [0, 3]) def test_success_exitcodes(pyfile, start_method, run_as, exit_code): @@ -320,6 +313,7 @@ def test_success_exitcodes(pyfile, start_method, run_as, exit_code): sys.exit(exit_code) with debug.Session(start_method) as session: + session.expected_exit_code = some.int session.configure(run_as, code_to_debug, args=[repr(exit_code)], successExitCodes=[3]) session.send_request( "setExceptionBreakpoints", {"filters": ["uncaught"]} @@ -330,8 +324,6 @@ def test_success_exitcodes(pyfile, start_method, run_as, exit_code): session.wait_for_stop("exception") session.request_continue() - session.stop_debugging(exitCode=some.int) - @pytest.mark.parametrize("max_frames", ["default", "all", 10]) def test_exception_stack(pyfile, start_method, run_as, max_frames): @@ -366,6 +358,7 @@ def test_exception_stack(pyfile, start_method, run_as, max_frames): maxFrames = 10 with debug.Session(start_method) as session: + session.expected_exit_code = some.int session.configure( run_as, code_to_debug, maxExceptionStackFrames=maxFrames, @@ -402,5 +395,3 @@ def test_exception_stack(pyfile, start_method, run_as, max_frames): assert min_expected_lines <= stack_line_count <= max_expected_lines session.request_continue() - - session.stop_debugging(exitCode=some.int) diff --git a/tests/ptvsd/server/test_exclude_rules.py b/tests/ptvsd/server/test_exclude_rules.py index 3e7fba00..bc9a1c30 100644 --- a/tests/ptvsd/server/test_exclude_rules.py +++ b/tests/ptvsd/server/test_exclude_rules.py @@ -51,7 +51,6 @@ def test_exceptions_and_exclude_rules( session.start_debugging() # No exceptions should be seen. - session.stop_debugging() @pytest.mark.parametrize("scenario", ["exclude_code_to_debug", "exclude_callback_dir"]) @@ -188,5 +187,3 @@ def test_exceptions_and_partial_exclude_rules(pyfile, start_method, run_as, scen else: pytest.fail(scenario) - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_flask.py b/tests/ptvsd/server/test_flask.py index 41ed8a55..fbd8100c 100644 --- a/tests/ptvsd/server/test_flask.py +++ b/tests/ptvsd/server/test_flask.py @@ -28,25 +28,23 @@ class lines: app_py = code.get_marked_line_numbers(paths.app_py) -def _initialize_session(session, multiprocess=False): - session.env.update({ +def _initialize_session(session, multiprocess=False, exit_code=0): + env = { "FLASK_APP": paths.app_py, "FLASK_ENV": "development", "FLASK_DEBUG": "1" if multiprocess else "0", - }) + } if platform.system() != "Windows": locale = "en_US.utf8" if platform.system() == "Linux" else "en_US.UTF-8" - session.env.update({"LC_ALL": locale, "LANG": locale}) + env.update({"LC_ALL": locale, "LANG": locale}) - session.program_args = ["run", "--port", str(flask.port)] + args = ["run"] if not multiprocess: - session.program_args[1:1] = ["--no-debugger", "--no-reload", "--with-threads"] + args += ["--no-debugger", "--no-reload", "--with-threads"] + args += ["--port", str(flask.port)] - session.debug_options |= {"Jinja"} - if multiprocess: - session.debug_options |= {"Multiprocess"} - - session.configure("module", "flask", cwd=paths.flask1) + session.expected_exit_code = exit_code + session.configure("module", "flask", cwd=paths.flask1, jinja=True, multiprocess=multiprocess, args=args, env=env) @pytest.mark.parametrize("start_method", [start_methods.Launch, start_methods.AttachSocketCmdLine]) @@ -59,7 +57,7 @@ def test_flask_breakpoint_no_multiproc(start_method, bp_target): bp_var_content = compat.force_str("Flask-Jinja-Test") with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) # No clean way to kill Flask server session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() @@ -91,15 +89,11 @@ def test_flask_breakpoint_no_multiproc(start_method, bp_target): session.request_continue() assert bp_var_content in home_request.response_text() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Flask server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch, start_methods.AttachSocketCmdLine]) def test_flask_template_exception_no_multiproc(start_method): with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) # No clean way to kill Flask server session.request("setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}) session.start_debugging() @@ -148,10 +142,6 @@ def test_flask_template_exception_no_multiproc(start_method): session.wait_for_stop("exception") session.request_continue() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Flask server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch, start_methods.AttachSocketCmdLine]) @pytest.mark.parametrize("exc_type", ["handled", "unhandled"]) @@ -159,7 +149,7 @@ def test_flask_exception_no_multiproc(start_method, exc_type): exc_line = lines.app_py["exc_" + exc_type] with debug.Session(start_method) as session: - _initialize_session(session) + _initialize_session(session, exit_code=some.int) # No clean way to kill Flask server session.request("setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}) session.start_debugging() @@ -202,10 +192,6 @@ def test_flask_exception_no_multiproc(start_method, exc_type): session.request_continue() - session.stop_debugging( - exitCode=some.int, # No clean way to kill Flask server - ) - @pytest.mark.parametrize("start_method", [start_methods.Launch]) def test_flask_breakpoint_multiproc(start_method): @@ -213,7 +199,8 @@ def test_flask_breakpoint_multiproc(start_method): bp_var_content = compat.force_str("Flask-Jinja-Test") with debug.Session(start_method) as parent_session: - _initialize_session(parent_session, multiprocess=True) + # No clean way to kill Flask server + _initialize_session(parent_session, multiprocess=True, exit_code=some.int) parent_session.set_breakpoints(paths.app_py, [bp_line]) parent_session.start_debugging() @@ -248,8 +235,3 @@ def test_flask_breakpoint_multiproc(start_method): child_session.request_continue() assert bp_var_content in home_request.response_text() - - child_session.stop_debugging() - parent_session.stop_debugging( - exitCode=some.int, # No clean way to kill Flask server - ) diff --git a/tests/ptvsd/server/test_justmycode.py b/tests/ptvsd/server/test_justmycode.py index 5d748eda..00ba585e 100644 --- a/tests/ptvsd/server/test_justmycode.py +++ b/tests/ptvsd/server/test_justmycode.py @@ -19,9 +19,7 @@ def test_justmycode_frames(pyfile, start_method, run_as, jmc): print("break here") # @bp with debug.Session(start_method) as session: - if not jmc: - session.debug_options |= {"DebugStdLib"} - session.configure(run_as, code_to_debug) + session.configure(run_as, code_to_debug, justMyCode=bool(jmc)) bp_line = code_to_debug.lines["bp"] actual_bps = session.set_breakpoints(code_to_debug, [bp_line]) @@ -54,5 +52,3 @@ def test_justmycode_frames(pyfile, start_method, run_as, jmc): # 'continue' should terminate the debuggee session.request_continue() - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_log.py b/tests/ptvsd/server/test_log.py index c77ff2ac..812ec5ed 100644 --- a/tests/ptvsd/server/test_log.py +++ b/tests/ptvsd/server/test_log.py @@ -8,7 +8,7 @@ import contextlib import pytest from ptvsd.common import compat -from tests import debug +from tests import debug, start_methods @contextlib.contextmanager @@ -37,10 +37,10 @@ def test_log_cli(pyfile, tmpdir, start_method, run_as, cli): session.env["PTVSD_LOG_DIR"] = str(tmpdir) session.configure(run_as, code_to_debug) session.start_debugging() - session.stop_debugging() -def test_log_api(pyfile, tmpdir, run_as): +@pytest.mark.parametrize("start_method", [start_methods.CustomServer]) +def test_log_api(pyfile, tmpdir, start_method, run_as): @pyfile def code_to_debug(): from debug_me import backchannel, ptvsd @@ -49,7 +49,7 @@ def test_log_api(pyfile, tmpdir, run_as): ptvsd.wait_for_attach() log_dir = compat.filename(tmpdir) - with debug.Session("custom_server", backchannel=True) as session: + with debug.Session(start_method, backchannel=True) as session: backchannel = session.backchannel @session.before_connect @@ -59,4 +59,3 @@ def test_log_api(pyfile, tmpdir, run_as): with check_logs(tmpdir, session): session.configure(run_as, code_to_debug) session.start_debugging() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_multiproc.py b/tests/ptvsd/server/test_multiproc.py index ddc682a3..cf207ae7 100644 --- a/tests/ptvsd/server/test_multiproc.py +++ b/tests/ptvsd/server/test_multiproc.py @@ -132,11 +132,7 @@ def test_multiprocessing(pyfile, start_method, run_as): parent_backchannel.send("continue") - grandchild_session.stop_debugging() - child_session.stop_debugging() - - assert parent_backchannel.receive() == "done" - parent_session.stop_debugging() + assert parent_backchannel.receive() == "done" @pytest.mark.timeout(30) @@ -170,11 +166,9 @@ def test_subprocess(pyfile, start_method, run_as): ) process.wait() - with debug.Session(start_method) as parent_session: - parent_backchannel = parent_session.setup_backchannel() - parent_session.program_args += [child] - parent_session.debug_options |= {"Multiprocess"} - parent_session.configure(run_as, parent) + with debug.Session(start_method, backchannel=True) as parent_session: + parent_backchannel = parent_session.backchannel + parent_session.configure(run_as, parent, subProcess=True, args=[child]) parent_session.start_debugging() root_start_request, = parent_session.all_occurrences_of( @@ -207,9 +201,6 @@ def test_subprocess(pyfile, start_method, run_as): child_argv = parent_backchannel.receive() assert child_argv == [child, "--arg1", "--arg2", "--arg3"] - child_session.wait_for_termination() - parent_session.stop_debugging() - @pytest.mark.timeout(30) @pytest.mark.skipif( @@ -242,20 +233,24 @@ def test_autokill(pyfile, start_method, run_as): ) backchannel.receive() - with debug.Session(start_method) as parent_session: - parent_backchannel = parent_session.setup_backchannel() - parent_session.program_args += [child] - parent_session.debug_options |= {"Multiprocess"} - parent_session.configure(run_as, parent) + with debug.Session(start_method, backchannel=True) as parent_session: + parent_backchannel = parent_session.backchannel + expected_exit_code = some.int if parent_session.start_method.method == "launch" else 0 + parent_session.expected_exit_code = expected_exit_code + parent_session.configure( + run_as, + parent, + subProcess=True, + args=[child], + ) parent_session.start_debugging() - expected_exit_code = 0 + with parent_session.attach_to_next_subprocess() as child_session: child_session.start_debugging() if parent_session.start_method.method == "launch": # In launch scenario, terminate the parent process by disconnecting from it. - expected_exit_code = some.int try: parent_session.request("disconnect") except messaging.NoMoreMessages: @@ -264,12 +259,8 @@ def test_autokill(pyfile, start_method, run_as): parent_session.wait_for_disconnect() else: # In attach scenario, just let the parent process run to completion. - expected_exit_code = 0 parent_backchannel.send(None) - child_session.stop_debugging() - parent_session.stop_debugging(exitCode=expected_exit_code) - @pytest.mark.skipif( sys.version_info < (3, 0) and (platform.system() != "Windows"), reason="Bug #935" @@ -327,5 +318,3 @@ def test_argv_quoting(pyfile, start_method, run_as): expected_args = backchannel.receive() actual_args = backchannel.receive() assert expected_args == actual_args - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_output.py b/tests/ptvsd/server/test_output.py index 4bc9e93a..294eccf6 100644 --- a/tests/ptvsd/server/test_output.py +++ b/tests/ptvsd/server/test_output.py @@ -28,12 +28,11 @@ def test_with_no_output(pyfile, start_method, run_as): session.start_debugging() session.wait_for_stop("breakpoint") session.request_continue() - session.stop_debugging() - assert not session.output("stdout") - assert not session.output("stderr") - assert not session.captured_stdout() - assert not session.captured_stderr() + assert not session.output("stdout") + assert not session.output("stderr") + assert not session.captured_stdout() + assert not session.captured_stderr() def test_with_tab_in_output(pyfile, start_method, run_as): @@ -52,9 +51,8 @@ def test_with_tab_in_output(pyfile, start_method, run_as): session.start_debugging() session.wait_for_stop() session.request_continue() - session.stop_debugging() - assert session.output("stdout").startswith("Hello\tWorld") + assert session.output("stdout").startswith("Hello\tWorld") @pytest.mark.parametrize("redirect", ["enabled", "disabled"]) @@ -77,9 +75,8 @@ def test_redirect_output(pyfile, start_method, run_as, redirect): session.wait_for_stop() session.request_continue() - session.stop_debugging() - if redirect == "enabled": - assert session.output("stdout") == "111\n222\n333\n444\n" - else: - assert not session.output("stdout") + if redirect == "enabled": + assert session.output("stdout") == "111\n222\n333\n444\n" + else: + assert not session.output("stdout") diff --git a/tests/ptvsd/server/test_path_mapping.py b/tests/ptvsd/server/test_path_mapping.py index 0afe63ce..119ccc54 100644 --- a/tests/ptvsd/server/test_path_mapping.py +++ b/tests/ptvsd/server/test_path_mapping.py @@ -62,7 +62,6 @@ def test_client_ide_from_path_mapping_linux_backend( ) session.request_continue() - session.stop_debugging() def test_with_dot_remote_root(pyfile, long_tmpdir, start_method, run_as): @@ -107,7 +106,6 @@ def test_with_dot_remote_root(pyfile, long_tmpdir, start_method, run_as): ) session.request_continue() - session.stop_debugging() def test_with_path_mappings(pyfile, long_tmpdir, start_method, run_as): @@ -192,4 +190,3 @@ def test_with_path_mappings(pyfile, long_tmpdir, start_method, run_as): assert "def call_me_back(callback):" in source["content"] session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_run.py b/tests/ptvsd/server/test_run.py index efb24422..c00a4625 100644 --- a/tests/ptvsd/server/test_run.py +++ b/tests/ptvsd/server/test_run.py @@ -51,8 +51,6 @@ def test_run(pyfile, start_method, run_as): some.str.matching(re.escape(expected_ptvsd_path) + r"(c|o)?") ) - session.stop_debugging() - def test_run_submodule(): with debug.Session("launch") as session: @@ -64,7 +62,6 @@ def test_run_submodule(): some.dict.containing({"category": "stdout", "output": "three"}), ) ) - session.stop_debugging() @pytest.mark.parametrize("run_as", ["program", "module", "code"]) @@ -89,13 +86,12 @@ def test_nodebug(pyfile, run_as): backchannel.send(None) # Breakpoint shouldn't be hit. - session.stop_debugging() - session.expect_realized( - Event( - "output", some.dict.containing({"category": "stdout", "output": "ok"}) - ) + session.expect_realized( + Event( + "output", some.dict.containing({"category": "stdout", "output": "ok"}) ) + ) @pytest.mark.parametrize("run_as", ["script", "module"]) @@ -127,4 +123,3 @@ def test_run_vs(pyfile, run_as): session.start_debugging() assert backchannel.receive() == "ok" - session.stop_debugging() diff --git a/tests/ptvsd/server/test_set_expression.py b/tests/ptvsd/server/test_set_expression.py index 4155ce3c..d21c2119 100644 --- a/tests/ptvsd/server/test_set_expression.py +++ b/tests/ptvsd/server/test_set_expression.py @@ -57,5 +57,3 @@ def test_set_expression(pyfile, start_method, run_as): session.request_continue() assert backchannel.receive() == 1000 - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_start_stop.py b/tests/ptvsd/server/test_start_stop.py index d0d72b20..1aacf5cc 100644 --- a/tests/ptvsd/server/test_start_stop.py +++ b/tests/ptvsd/server/test_start_stop.py @@ -35,9 +35,8 @@ def test_wait_on_normal_exit_enabled(pyfile, start_method, run_as): session.request_continue() session.process.stdin.write(b" \r\n") - session.stop_debugging() - assert any(s.startswith("Press") for s in session.stdout_lines("utf-8")) + assert any(s.startswith("Press") for s in session.stdout_lines("utf-8")) @pytest.mark.parametrize("start_method", [start_methods.Launch]) @@ -57,6 +56,7 @@ def test_wait_on_abnormal_exit_enabled(pyfile, start_method, run_as): with debug.Session(start_method, backchannel=True) as session: backchannel = session.backchannel + session.expected_exit_code = 12345 session.configure( run_as, code_to_debug, waitOnAbnormalExit=True, @@ -69,9 +69,8 @@ def test_wait_on_abnormal_exit_enabled(pyfile, start_method, run_as): assert backchannel.receive() == "done" session.process.stdin.write(b" \r\n") - session.stop_debugging(exitCode=12345) - assert any(s.startswith("Press") for s in session.stdout_lines("utf-8")) + assert any(s.startswith("Press") for s in session.stdout_lines("utf-8")) @pytest.mark.parametrize("start_method", [start_methods.Launch]) @@ -96,4 +95,3 @@ def test_exit_normally_with_wait_on_abnormal_exit_enabled(pyfile, start_method, session.wait_for_termination() assert backchannel.receive() == "done" - session.stop_debugging() diff --git a/tests/ptvsd/server/test_step.py b/tests/ptvsd/server/test_step.py index 578af277..fa445803 100644 --- a/tests/ptvsd/server/test_step.py +++ b/tests/ptvsd/server/test_step.py @@ -78,4 +78,3 @@ def test_set_next_statement(pyfile, start_method, run_as): assert line == line_numbers["inner2"] session.send_request("continue").wait_for_response() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_stop_on_entry.py b/tests/ptvsd/server/test_stop_on_entry.py index a4080e8e..d28cda8a 100644 --- a/tests/ptvsd/server/test_stop_on_entry.py +++ b/tests/ptvsd/server/test_stop_on_entry.py @@ -43,8 +43,4 @@ def test_stop_on_entry(pyfile, run_as, breakpoint): assert hit.frames[0]["source"]["path"] == some.path(code_to_debug) session.request_continue() - session.wait_for_termination() - assert backchannel.receive() == "done" - - session.stop_debugging() diff --git a/tests/ptvsd/server/test_threads.py b/tests/ptvsd/server/test_threads.py index 0ce2a0af..35e89d8b 100644 --- a/tests/ptvsd/server/test_threads.py +++ b/tests/ptvsd/server/test_threads.py @@ -50,7 +50,6 @@ def test_thread_count(pyfile, start_method, run_as, count): assert len(resp_threads.body["threads"]) == count session.request_continue() - session.stop_debugging() @pytest.mark.skipif( @@ -110,4 +109,3 @@ def test_debug_this_thread(pyfile, start_method, run_as): session.wait_for_stop() session.request_continue() - session.stop_debugging() diff --git a/tests/ptvsd/server/test_vs_specific.py b/tests/ptvsd/server/test_vs_specific.py index 57bf392d..52e4e830 100644 --- a/tests/ptvsd/server/test_vs_specific.py +++ b/tests/ptvsd/server/test_vs_specific.py @@ -27,7 +27,6 @@ def test_stack_format(pyfile, start_method, run_as, module, line): print("break here") # @bp with debug.Session(start_method) as session: - session.ignore_unobserved += [Event("stopped")] session.configure(run_as, code_to_debug) session.set_breakpoints(test_module, [test_module.lines["bp"]]) session.start_debugging() @@ -50,7 +49,6 @@ def test_stack_format(pyfile, start_method, run_as, module, line): assert module == (frames[0]["name"].find("test_module") > -1) session.request_continue() - session.stop_debugging() def test_module_events(pyfile, start_method, run_as): @@ -74,7 +72,6 @@ def test_module_events(pyfile, start_method, run_as): do_something() with debug.Session(start_method) as session: - session.ignore_unobserved += [Event("stopped")] session.configure(run_as, test_code) session.set_breakpoints(module2, [module2.lines["bp"]]) session.start_debugging() @@ -91,4 +88,3 @@ def test_module_events(pyfile, start_method, run_as): ] session.request_continue() - session.stop_debugging() diff --git a/tests/start_methods.py b/tests/start_methods.py index 9b17bbd6..41340be9 100644 --- a/tests/start_methods.py +++ b/tests/start_methods.py @@ -40,17 +40,16 @@ class DebugStartBase(object): self.method = method self.captured_output = helpers.CapturedOutput(self.session) self.debuggee_process = None - - def configure(self, run_as, target, **kwargs): - pass + self.expected_exit_code = None def start_debugging(self, **kwargs): pass - def wait_for_debuggee(self, exit_code): - if exit_code is not None: + def wait_for_debuggee(self): + # TODO: Exit should not be restricted to launch tests only + if self.expected_exit_code is not None and 'launch' in self.method: exited = self.session.wait_for_next_event("exited", freeze=False) - assert exited == some.dict.containing({"exitCode": exit_code}) + assert exited == some.dict.containing({"exitCode": self.expected_exit_code}) self.session.wait_for_next_event("terminated") @@ -274,9 +273,6 @@ class Launch(DebugStartBase): self._launch_request.wait_for_response(freeze=False) self._wait_for_process_event() - def wait_for_debuggee(self, exit_code=0): - super(Launch, self).wait_for_debuggee(exit_code) - def run_in_terminal(self, request): args = request("args", json.array(unicode)) cwd = request("cwd", unicode) @@ -503,9 +499,10 @@ class AttachSocketCmdLine(AttachBase): pythonPath=sys.executable, args=[], cwd=None, - env=os.environ.copy(), + env=None, **kwargs ): + env = {} if env is None else dict(env) self._attach_args = self._build_attach_args({}, run_as, target, **kwargs) cli_args = [pythonPath]