From 6feb7f9fe1499cc0de66b5bbce506a5f57ff2fef Mon Sep 17 00:00:00 2001 From: Pavel Minaev Date: Mon, 17 Feb 2020 18:21:13 -0800 Subject: [PATCH] Fix https://github.com/microsoft/ptvsd/issues/2079: sys.argv sends in unicode instead of string Convert sys.argv to Unicode as it is being parsed, and do not touch the remaining args. --- src/debugpy/server/cli.py | 17 +++++++++----- tests/debugpy/server/test_cli.py | 38 ++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/debugpy/server/cli.py b/src/debugpy/server/cli.py index 09dabc8c..3128886a 100644 --- a/src/debugpy/server/cli.py +++ b/src/debugpy/server/cli.py @@ -171,9 +171,16 @@ switches = [ # fmt: on -def parse(args): +def consume_argv(): + while len(sys.argv) >= 2: + value = sys.argv[1] + del sys.argv[1] + yield value + + +def parse_argv(): seen = set() - it = (compat.filename(arg) for arg in args) + it = consume_argv() while True: try: @@ -215,14 +222,12 @@ def parse(args): assert options.target_kind is not None assert options.address is not None - return it - def start_debugging(argv_0): # We need to set up sys.argv[0] before invoking either listen() or connect(), # 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) + sys.argv[0] = compat.filename_str(argv_0) log.debug("sys.argv after patching: {0!r}", sys.argv) debugpy.configure(options.config) @@ -392,7 +397,7 @@ attach_pid_injected.attach(setup); def main(): original_argv = list(sys.argv) try: - sys.argv[1:] = parse(sys.argv[1:]) + parse_argv() except Exception as ex: print(HELP + "\nError: " + str(ex), file=sys.stderr) sys.exit(2) diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index 9a48a136..531606af 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -23,26 +23,30 @@ def cli(pyfile): from debugpy.server import cli try: - sys.argv[1:] = cli.parse(sys.argv[1:]) + cli.parse_argv() except Exception as exc: os.write(1, pickle.dumps(exc)) sys.exit(1) - else: - # We only care about options that correspond to public switches. - options = { - name: getattr(cli.options, name) - for name in [ - "address", - "config", - "log_to", - "log_to_stderr", - "mode", - "target", - "target_kind", - "wait_for_client", - ] - } - os.write(1, pickle.dumps([sys.argv[1:], options])) + + # Check that sys.argv has the correct type after parsing - there should be + # no bytes on Python 3, nor unicode on Python 2. + assert all(isinstance(s, str) for s in sys.argv) + + # We only care about options that correspond to public switches. + options = { + name: getattr(cli.options, name) + for name in [ + "address", + "config", + "log_to", + "log_to_stderr", + "mode", + "target", + "target_kind", + "wait_for_client", + ] + } + os.write(1, pickle.dumps([sys.argv[1:], options])) def parse(args): log.debug("Parsing argv: {0!r}", args)