Skip to content

Commit

Permalink
Fix microsoft#370: Terminal Keyboard Inputs not being accepted
Browse files Browse the repository at this point in the history
Make the debuggee process group the foreground group in its session.

Add a test for input(), and improve existing stdin test to cover more cases.
  • Loading branch information
int19h committed Aug 18, 2020
1 parent 57ec4c4 commit 3a364a4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 23 deletions.
31 changes: 26 additions & 5 deletions src/debugpy/launcher/debuggee.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,26 @@ def spawn(process_name, cmdline, env, redirect_output):
kwargs = {}

if sys.platform != "win32":
# Start the debuggee in a new process group, so that the launcher can kill
# the entire process tree later.
kwargs.update(preexec_fn=os.setpgrp)

def preexec_fn():
# Start the debuggee in a new process group, so that the launcher can
# kill the entire process tree later.
os.setpgrp()

# Make the new process group the foreground group in its session, so
# that it can interact with the terminal. The debuggee will receive
# SIGTTOU when tcsetpgrp() is called, and must ignore it.
hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN)
try:
tty = os.open("/dev/tty", os.O_RDWR)
try:
os.tcsetpgrp(tty, os.getpgrp())
finally:
os.close(tty)
finally:
signal.signal(signal.SIGTTOU, hdlr)

kwargs.update(preexec_fn=preexec_fn)

try:
global process
Expand Down Expand Up @@ -94,7 +111,9 @@ def spawn(process_name, cmdline, env, redirect_output):

# Setting this flag ensures that the job will be terminated by the OS once the
# launcher exits, even if it doesn't terminate the job explicitly.
job_info.BasicLimitInformation.LimitFlags |= winapi.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
job_info.BasicLimitInformation.LimitFlags |= (
winapi.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
)
winapi.kernel32.SetInformationJobObject(
job_handle,
winapi.JobObjectExtendedLimitInformation,
Expand All @@ -103,7 +122,9 @@ def spawn(process_name, cmdline, env, redirect_output):
)

process_handle = winapi.kernel32.OpenProcess(
winapi.PROCESS_TERMINATE | winapi.PROCESS_SET_QUOTA, False, process.pid
winapi.PROCESS_TERMINATE | winapi.PROCESS_SET_QUOTA,
False,
process.pid,
)

winapi.kernel32.AssignProcessToJobObject(job_handle, process_handle)
Expand Down
61 changes: 61 additions & 0 deletions tests/debugpy/test_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE in the project root
# for license information.

from __future__ import absolute_import, division, print_function, unicode_literals

import pytest

from tests import debug
from tests.debug import runners


@pytest.mark.parametrize("run", runners.all)
@pytest.mark.parametrize("redirect_output", ["", "redirect_output"])
def test_stdin_not_patched(pyfile, target, run, redirect_output):
@pyfile
def code_to_debug():
import sys
import debuggee
from debuggee import backchannel

debuggee.setup()
backchannel.send(sys.stdin is sys.__stdin__)

with debug.Session() as session:
session.config["redirectOutput"] = bool(redirect_output)

backchannel = session.open_backchannel()
with run(session, target(code_to_debug)):
pass

is_original_stdin = backchannel.receive()
assert is_original_stdin, "Expected sys.stdin and sys.__stdin__ to be the same."


@pytest.mark.parametrize("run", runners.all_launch_terminal + runners.all_attach)
@pytest.mark.parametrize("redirect_output", ["", "redirect_output"])
def test_input(pyfile, target, run, redirect_output):
@pyfile
def code_to_debug():
import debuggee
from debuggee import backchannel

try:
input = raw_input
except NameError:
pass

debuggee.setup()
backchannel.send(input())

with debug.Session() as session:
session.config["redirectOutput"] = bool(redirect_output)

backchannel = session.open_backchannel()
with run(session, target(code_to_debug)):
pass

session.debuggee.stdin.write(b"ok\n")
s = backchannel.receive()
assert s == "ok"
18 changes: 0 additions & 18 deletions tests/debugpy/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,6 @@ def code_to_debug():
)


def test_stdin_not_patched(pyfile, target, run):
@pyfile
def code_to_debug():
import sys
import debuggee
from debuggee import backchannel

debuggee.setup()
backchannel.send(sys.stdin == sys.__stdin__)

with debug.Session() as session:
backchannel = session.open_backchannel()
with run(session, target(code_to_debug)):
pass
is_original_stdin = backchannel.receive()
assert is_original_stdin, 'Expected sys.stdin and sys.__stdin__ to be the same.'


if sys.platform == "win32":

@pytest.mark.parametrize("redirect_output", ["", "redirect_output"])
Expand Down

0 comments on commit 3a364a4

Please sign in to comment.