Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix terminal size in tox commands (#2999) #3139

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog/2999.bugfix.rst
@@ -0,0 +1 @@
Fix terminal size of tox subcommands (fixes ipython, ipdb, prompt_toolkit, ...).
2 changes: 1 addition & 1 deletion src/tox/execute/local_sub_process/__init__.py
Expand Up @@ -314,7 +314,7 @@ def _pty(key: str) -> tuple[int, int] | None:
# adjust sub-process terminal size
columns, lines = shutil.get_terminal_size(fallback=(-1, -1))
if columns != -1 and lines != -1:
size = struct.pack("HHHH", columns, lines, 0, 0)
size = struct.pack("HHHH", lines, columns, 0, 0)
fcntl.ioctl(child, termios.TIOCSWINSZ, size)

return main, child
Expand Down
36 changes: 36 additions & 0 deletions tests/execute/local_subprocess/test_local_subprocess.py
Expand Up @@ -19,6 +19,7 @@

from tox.execute.api import ExecuteOptions, Outcome
from tox.execute.local_sub_process import SIG_INTERRUPT, LocalSubProcessExecuteInstance, LocalSubProcessExecutor
from tox.execute.local_sub_process.read_via_thread_unix import ReadViaThreadUnix
from tox.execute.request import ExecuteRequest, StdinSource
from tox.execute.stream import SyncWrite
from tox.report import NamedBytesIO
Expand Down Expand Up @@ -140,6 +141,41 @@ def test_local_execute_write_a_lot(os_env: dict[str, str]) -> None:
assert outcome.err == expected_err, expected_err[len(outcome.err) :]


@pytest.mark.skipif(sys.platform == "win32", reason="Unix terminal size test")
def test_local_execute_terminal_size(os_env: dict[str, str], monkeypatch: MonkeyPatch) -> None:
"""Regression test for #2999 - check terminal size is set correctly in tox subprocess."""
import pty

terminal_size = os.terminal_size((84, 42))
main, child = pty.openpty() # type: ignore[attr-defined, unused-ignore]
# Use ReadViaThreadUnix to help with debugging the test itself.
pipe_out = ReadViaThreadUnix(main, sys.stdout.buffer.write, name="testout", drain=True) # type: ignore[arg-type]
with pipe_out, monkeypatch.context() as monkey, open(child, "w") as stdout_mock: # noqa: PTH123
# Switch stdout with test pty
monkey.setattr(sys, "stdout", stdout_mock)
monkey.setenv("COLUMNS", "84")
monkey.setenv("LINES", "42")

executor = LocalSubProcessExecutor(colored=False)
request = ExecuteRequest(
cmd=[sys.executable, "-c", "import os; print(os.get_terminal_size())"],
cwd=Path(),
env=os_env,
stdin=StdinSource.OFF,
run_id="",
)
out_err = FakeOutErr()
with executor.call(request, show=False, out_err=out_err.out_err, env=MagicMock()) as status:
while status.exit_code is None: # pragma: no branch
status.wait()
outcome = status.outcome
assert outcome is not None
assert bool(outcome), outcome
expected_out = f"{terminal_size!r}\r\n"
assert outcome.out == expected_out, expected_out[len(outcome.out) :]
assert not outcome.err


def test_local_execute_basic_fail(capsys: CaptureFixture, caplog: LogCaptureFixture, monkeypatch: MonkeyPatch) -> None:
monkeypatch.chdir(Path(__file__).parents[3])
caplog.set_level(logging.NOTSET)
Expand Down