Skip to content

Commit 6feb7f9

Browse files
committed
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.
1 parent 9fe1be6 commit 6feb7f9

2 files changed

Lines changed: 32 additions & 23 deletions

File tree

src/debugpy/server/cli.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,16 @@ def do(arg, it):
171171
# fmt: on
172172

173173

174-
def parse(args):
174+
def consume_argv():
175+
while len(sys.argv) >= 2:
176+
value = sys.argv[1]
177+
del sys.argv[1]
178+
yield value
179+
180+
181+
def parse_argv():
175182
seen = set()
176-
it = (compat.filename(arg) for arg in args)
183+
it = consume_argv()
177184

178185
while True:
179186
try:
@@ -215,14 +222,12 @@ def parse(args):
215222
assert options.target_kind is not None
216223
assert options.address is not None
217224

218-
return it
219-
220225

221226
def start_debugging(argv_0):
222227
# We need to set up sys.argv[0] before invoking either listen() or connect(),
223228
# because they use it to report the "process" event. Thus, we can't rely on
224229
# run_path() and run_module() doing that, even though they will eventually.
225-
sys.argv[0] = compat.filename(argv_0)
230+
sys.argv[0] = compat.filename_str(argv_0)
226231
log.debug("sys.argv after patching: {0!r}", sys.argv)
227232

228233
debugpy.configure(options.config)
@@ -392,7 +397,7 @@ def attach_to_pid():
392397
def main():
393398
original_argv = list(sys.argv)
394399
try:
395-
sys.argv[1:] = parse(sys.argv[1:])
400+
parse_argv()
396401
except Exception as ex:
397402
print(HELP + "\nError: " + str(ex), file=sys.stderr)
398403
sys.exit(2)

tests/debugpy/server/test_cli.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,30 @@ def cli_parser():
2323
from debugpy.server import cli
2424

2525
try:
26-
sys.argv[1:] = cli.parse(sys.argv[1:])
26+
cli.parse_argv()
2727
except Exception as exc:
2828
os.write(1, pickle.dumps(exc))
2929
sys.exit(1)
30-
else:
31-
# We only care about options that correspond to public switches.
32-
options = {
33-
name: getattr(cli.options, name)
34-
for name in [
35-
"address",
36-
"config",
37-
"log_to",
38-
"log_to_stderr",
39-
"mode",
40-
"target",
41-
"target_kind",
42-
"wait_for_client",
43-
]
44-
}
45-
os.write(1, pickle.dumps([sys.argv[1:], options]))
30+
31+
# Check that sys.argv has the correct type after parsing - there should be
32+
# no bytes on Python 3, nor unicode on Python 2.
33+
assert all(isinstance(s, str) for s in sys.argv)
34+
35+
# We only care about options that correspond to public switches.
36+
options = {
37+
name: getattr(cli.options, name)
38+
for name in [
39+
"address",
40+
"config",
41+
"log_to",
42+
"log_to_stderr",
43+
"mode",
44+
"target",
45+
"target_kind",
46+
"wait_for_client",
47+
]
48+
}
49+
os.write(1, pickle.dumps([sys.argv[1:], options]))
4650

4751
def parse(args):
4852
log.debug("Parsing argv: {0!r}", args)

0 commit comments

Comments
 (0)