From 9692e9770ff252e49f1616e7d98ea62c7f12c8d0 Mon Sep 17 00:00:00 2001 From: Pavel Minaev Date: Fri, 13 Sep 2019 15:12:12 -0700 Subject: [PATCH] Replace --multiprocess CLI switch with --no-subprocesses to reflect multiproc debugging being on by default. Fix test_parse_args accordingly. --- src/ptvsd/__main__.py | 6 +- src/ptvsd/server/cli.py | 102 +++++++++++++------------- tests/ptvsd/server/test_parse_args.py | 22 +++--- 3 files changed, 66 insertions(+), 64 deletions(-) diff --git a/src/ptvsd/__main__.py b/src/ptvsd/__main__.py index 15f95a3d..7b4d8f16 100644 --- a/src/ptvsd/__main__.py +++ b/src/ptvsd/__main__.py @@ -7,8 +7,6 @@ from __future__ import absolute_import, print_function, unicode_literals import os.path import sys -__all__ = ["main"] - # Force absolute path on Python 2. __file__ = os.path.abspath(__file__) @@ -41,5 +39,5 @@ if __name__ == "__main__": import ptvsd # noqa del sys.path[0] - from ptvsd.server import main - main.main() + from ptvsd.server import cli + cli.main() diff --git a/src/ptvsd/server/cli.py b/src/ptvsd/server/cli.py index 4f33d1c3..fb17ee0a 100644 --- a/src/ptvsd/server/cli.py +++ b/src/ptvsd/server/cli.py @@ -26,7 +26,7 @@ See https://aka.ms/ptvsd for documentation. Usage: ptvsd [--client] --host
[--port ] [--wait] - [--multiprocess] + [--no-subprocesses] [--log-dir ] [--log-stderr] {1} """.format( @@ -69,9 +69,9 @@ def set_arg(varname, parser=(lambda x: x), options=options): return action -def set_true(varname, options=options): +def set_const(varname, value, options=options): def do(arg, it): - setattr(options, varname, True) + setattr(options, varname, value) return do @@ -97,26 +97,26 @@ switches = [ # ====== =========== ====== ======== # Switches that are documented for use by end users. - (('-?', '-h', '--help'), None, print_help_and_exit, False), - (('-V', '--version'), None, print_version_and_exit, False), - ('--client', None, set_true('client'), False), - ('--host', '
', set_arg('host'), True), - ('--port', '', set_arg('port', port), False), - ('--wait', None, set_true('wait'), False), - ('--multiprocess', None, set_true('multiprocess'), False), - ('--log-dir', '', set_arg('log_dir', options=common_opts), False), - ('--log-stderr', None, set_log_stderr(), False), + (("-?", "-h", "--help"), None, print_help_and_exit, False), + (("-V", "--version"), None, print_version_and_exit, False), + ("--client", None, set_const("client", True), False), + ("--host", "
", set_arg("host"), True), + ("--port", "", set_arg("port", port), False), + ("--wait", None, set_const("wait", True), False), + ("--no-subprocesses", None, set_const("multiprocess", False), False), + ("--log-dir", "", set_arg("log_dir", options=common_opts), False), + ("--log-stderr", None, set_log_stderr(), False), # Switches that are used internally by the IDE or ptvsd itself. - ('--subprocess-of', '', set_arg('subprocess_of', pid), False), - ('--subprocess-notify', '', set_arg('subprocess_notify', port), False), + ("--subprocess-of", "", set_arg("subprocess_of", pid), False), + ("--subprocess-notify", "", set_arg("subprocess_notify", port), False), - # Targets. The '' entry corresponds to positional command line arguments, + # Targets. The "" entry corresponds to positional command line arguments, # i.e. the ones not preceded by any switch name. - ('', '', set_target('file', positional=True), False), - ('-m', '', set_target('module'), False), - ('-c', '', set_target('code'), False), - ('--pid', '', set_target('pid', pid), False), + ("", "", set_target("file", positional=True), False), + ("-m", "", set_target("module"), False), + ("-c", "", set_target("code"), False), + ("--pid", "", set_target("pid", pid), False), ] # fmt: on @@ -174,7 +174,7 @@ def parse(args): def setup_debug_server(argv_0): # We need to set up sys.argv[0] before invoking attach() or enable_attach(), - # because they use it to report the 'process' event. Thus, we can't rely on + # because they use it to report the "process" event. Thus, we can't rely on # run_path() and run_module() doing that, even though they will eventually. sys.argv[0] = compat.filename(argv_0) log.debug("sys.argv after patching: {0!r}", sys.argv) @@ -240,9 +240,9 @@ def run_module(): log.info("Running module {0!r}", target) # Docs say that runpy.run_module is equivalent to -m, but it's not actually - # the case for packages - -m sets __name__ to '__main__', but run_module sets - # it to `pkg.__main__`. This breaks everything that uses the standard pattern - # __name__ == '__main__' to detect being run as a CLI app. On the other hand, + # the case for packages - -m sets __name__ to "__main__", but run_module sets + # it to "pkg.__main__". This breaks everything that uses the standard pattern + # __name__ == "__main__" to detect being run as a CLI app. On the other hand, # runpy._run_module_as_main is a private function that actually implements -m. try: run_module_as_main = runpy._run_module_as_main @@ -266,7 +266,7 @@ def run_code(): def attach_to_pid(): - log.info('Attaching to process with ID {0}', options.target) + log.info("Attaching to process with ID {0}", options.target) pid = options.target host = options.host @@ -277,21 +277,24 @@ def attach_to_pid(): log_dir = "" try: - attach_pid_injected_dirname = os.path.join(os.path.dirname(ptvsd.__file__), 'server') + attach_pid_injected_dirname = os.path.join( + os.path.dirname(ptvsd.__file__), "server" + ) assert os.path.exists(attach_pid_injected_dirname) - log_dir = log_dir.replace('\\', '/') + log_dir = log_dir.replace("\\", "/") encode = lambda s: list(bytearray(s.encode("utf-8"))) setup = { - 'script': encode(attach_pid_injected_dirname), - 'host': encode(host), - 'port': port, - 'client': client, - 'log_dir': encode(log_dir), + "script": encode(attach_pid_injected_dirname), + "host": encode(host), + "port": port, + "client": client, + "log_dir": encode(log_dir), } - python_code = '''import sys; + python_code = """ +import sys; import codecs; decode = lambda s: codecs.utf_8_decode(bytearray(s))[0]; script_path = decode({script}); @@ -301,34 +304,35 @@ sys.path.remove(script_path); host = decode({host}); log_dir = decode({log_dir}) or None; attach_pid_injected.attach(port={port}, host=host, client={client}, log_dir=log_dir) -'''.replace('\r', '').replace('\n', '') +""" + python_code = python_code.replace("\r", "").replace("\n", "").format(**setup) + log.info("Code to be injected: \n{0}", python_code.replace(";", ";\n")) - python_code = python_code.format(**setup) - log.info("Code to be injected: \n{0}", python_code.replace(';', '\r\n')) - - # pydevd requires injected code to not contain any single quotes, double quotes or - # new lines. - assert "'" not in python_code, "Injected code should not contain any single quotes" - assert "\"" not in python_code, "Injected code should not contain any double quotes" - assert "\n" not in python_code, "Injected code should not contain any new lines" - assert "\r" not in python_code, "Injected code should not contain any " + # pydevd restriction on characters in injected code. + assert not ( + {'"', "'", "\r", "\n"} & set(python_code) + ), "Injected code should not contain any single quotes, double quots, or newlines." pydevd_attach_to_process_path = os.path.join( - os.path.dirname(pydevd.__file__), - 'pydevd_attach_to_process') + os.path.dirname(pydevd.__file__), "pydevd_attach_to_process" + ) assert os.path.exists(pydevd_attach_to_process_path) sys.path.append(pydevd_attach_to_process_path) - import add_code_to_python_process # noqa + show_debug_info_on_target_process = 0 # hard-coded (1 to debug) - log.info('Code injector begin') + log.info("Code injector begin") add_code_to_python_process.run_python_code( - pid, python_code, connect_debugger_tracing=True, show_debug_info=show_debug_info_on_target_process) - except: + pid, + python_code, + connect_debugger_tracing=True, + show_debug_info=show_debug_info_on_target_process, + ) + except Exception: raise log.exception() - log.info('Code injector exiting') + log.info("Code injector exiting") def main(): diff --git a/tests/ptvsd/server/test_parse_args.py b/tests/ptvsd/server/test_parse_args.py index e700ac16..8152a3dd 100644 --- a/tests/ptvsd/server/test_parse_args.py +++ b/tests/ptvsd/server/test_parse_args.py @@ -9,15 +9,15 @@ import pytest from ptvsd.common import log from ptvsd.common.compat import reload -from ptvsd.server import main, options +from ptvsd.server import cli, options @pytest.mark.parametrize("target_kind", ["file", "module", "code"]) @pytest.mark.parametrize("client", ["", "client"]) @pytest.mark.parametrize("wait", ["", "wait"]) -@pytest.mark.parametrize("multiproc", ["", "multiproc"]) +@pytest.mark.parametrize("subprocesses", ["", "subprocesses"]) @pytest.mark.parametrize("extra", ["", "extra"]) -def test_targets(target_kind, client, wait, multiproc, extra): +def test_targets(target_kind, client, wait, subprocesses, extra): args = ["--host", "localhost", "--port", "8888"] if client: @@ -26,8 +26,8 @@ def test_targets(target_kind, client, wait, multiproc, extra): if wait: args += ["--wait"] - if multiproc: - args += ["--multiprocess"] + if not subprocesses: + args += ["--no-subprocesses"] if target_kind == "file": target = "spam.py" @@ -61,7 +61,7 @@ def test_targets(target_kind, client, wait, multiproc, extra): log.debug("args = {0!r}", args) reload(options) - rest = list(main.parse(args)) + rest = list(cli.parse(args)) assert rest == extra expected_options = { @@ -70,7 +70,7 @@ def test_targets(target_kind, client, wait, multiproc, extra): "host": "localhost", "port": 8888, "wait": bool(wait), - "multiprocess": bool(multiproc), + "multiprocess": bool(subprocesses), } actual_options = {name: vars(options)[name] for name in expected_options} assert expected_options == actual_options @@ -79,22 +79,22 @@ def test_targets(target_kind, client, wait, multiproc, extra): def test_unsupported_arg(): reload(options) with pytest.raises(Exception): - main.parse(["--port", "8888", "--xyz", "123", "spam.py"]) + cli.parse(["--port", "8888", "--xyz", "123", "spam.py"]) def test_host_required(): reload(options) with pytest.raises(Exception): - main.parse(["--port", "8888", "-m", "spam"]) + cli.parse(["--port", "8888", "-m", "spam"]) def test_host_empty(): reload(options) - main.parse(["--host", "", "--port", "8888", "spam.py"]) + cli.parse(["--host", "", "--port", "8888", "spam.py"]) assert options.host == "" def test_port_default(): reload(options) - main.parse(["--host", "localhost", "spam.py"]) + cli.parse(["--host", "localhost", "spam.py"]) assert options.port == 5678