Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Lib/test/test_pty.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ def test_openpty(self):
s2 = _readline(master_fd)
self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2))

# skip (not expectedFailure) because the test still forks a real child
# process, which crashes on missing os.login_tty() inside the parallel
# test runner's worker process, corrupting its JSON reporting channel
# ("worker bug", reproducible under --slow-ci -j N; not under plain -m test).
@unittest.skip("TODO: RUSTPYTHON; pty.fork() calls os.login_tty(), which is not implemented")
def test_fork(self):
debug("calling pty.fork()")
pid, master_fd = pty.fork()
Expand Down Expand Up @@ -296,6 +301,11 @@ def test_master_read(self):

self.assertEqual(data, b"")

# skip (not expectedFailure) because the test still forks a real child
# process, which crashes on missing os.login_tty() inside the parallel
# test runner's worker process, corrupting its JSON reporting channel
# ("worker bug", reproducible under --slow-ci -j N; not under plain -m test).
@unittest.skip("TODO: RUSTPYTHON; pty.fork() calls os.login_tty(), which is not implemented")
def test_spawn_doesnt_hang(self):
# gh-140482: Do the test in a pty.fork() child to avoid messing
# with the interactive test runner's terminal settings.
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_termios.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ def writer():
'output was not resumed')
self.assertEqual(os.read(rfd, 1024), b'def')

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize'
def test_tcgetwinsize(self):
size = termios.tcgetwinsize(self.fd)
self.assertIsInstance(size, tuple)
Expand All @@ -230,22 +229,19 @@ def test_tcgetwinsize(self):
self.assertIsInstance(size[1], int)
self.assertEqual(termios.tcgetwinsize(self.stream), size)

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize'
def test_tcgetwinsize_errors(self):
self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetwinsize, self.bad_fd)
self.assertRaises(ValueError, termios.tcgetwinsize, -1)
self.assertRaises(OverflowError, termios.tcgetwinsize, 2**1000)
self.assertRaises(TypeError, termios.tcgetwinsize, object())
self.assertRaises(TypeError, termios.tcgetwinsize)

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize'
def test_tcsetwinsize(self):
size = termios.tcgetwinsize(self.fd)
termios.tcsetwinsize(self.fd, size)
termios.tcsetwinsize(self.fd, list(size))
termios.tcsetwinsize(self.stream, size)

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize'
def test_tcsetwinsize_errors(self):
size = termios.tcgetwinsize(self.fd)
self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size[:-1])
Expand Down
24 changes: 24 additions & 0 deletions crates/host_env/src/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,27 @@ pub fn tcflush(fd: i32, queue: i32) -> std::io::Result<()> {
pub fn tcflow(fd: i32, action: i32) -> std::io::Result<()> {
::termios::tcflow(fd, action)
}

pub fn tcgetwinsize(fd: i32) -> std::io::Result<(u16, u16)> {
let mut size: libc::winsize = unsafe { core::mem::zeroed() };
let ret = unsafe { libc::ioctl(fd, TIOCGWINSZ as _, &mut size) };
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
Ok((size.ws_row, size.ws_col))
}

pub fn tcsetwinsize(fd: i32, row: u16, col: u16) -> std::io::Result<()> {
let mut size: libc::winsize = unsafe { core::mem::zeroed() };
let ret = unsafe { libc::ioctl(fd, TIOCGWINSZ as _, &mut size) };
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
size.ws_row = row;
size.ws_col = col;
let ret = unsafe { libc::ioctl(fd, TIOCSWINSZ as _, &size) };
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
22 changes: 22 additions & 0 deletions crates/stdlib/src/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,28 @@ mod termios {
Ok(())
}

#[pyfunction]
fn tcgetwinsize(Fildes(fd): Fildes, vm: &VirtualMachine) -> PyResult<(u16, u16)> {
let size = host_termios::tcgetwinsize(fd).map_err(|e| termios_error(e, vm))?;
Ok(size)
}

#[pyfunction]
fn tcsetwinsize(Fildes(fd): Fildes, size: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let seq = size.try_sequence(vm)?;
if seq.length(vm)? != 2 {
return Err(vm.new_type_error("tcsetwinsize: size must be a 2 element sequence"));
}
Comment on lines +278 to +282

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size also is looking like a pair of number, always.

Suggested change
fn tcsetwinsize(Fildes(fd): Fildes, size: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let seq = size.try_sequence(vm)?;
if seq.length(vm)? != 2 {
return Err(vm.new_type_error("tcsetwinsize: size must be a 2 element sequence"));
}
fn tcsetwinsize(Fildes(fd): Fildes, size: (u16, u16), vm: &VirtualMachine) -> PyResult<()> {
let (row, col) = size;

@sigmaith sigmaith Jul 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it locally, and it behaves differently via FromArgs impl than a tuple or list argument would.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, you are right. I am sorry.

let row = seq.get_item(0, vm)?;
let col = seq.get_item(1, vm)?;

let row: u16 = row.try_index(vm)?.try_to_primitive(vm)?;
let col: u16 = col.try_index(vm)?.try_to_primitive(vm)?;

host_termios::tcsetwinsize(fd, row, col).map_err(|e| termios_error(e, vm))?;
Ok(())
}

fn termios_error(err: std::io::Error, vm: &VirtualMachine) -> PyBaseExceptionRef {
vm.new_os_subtype_error(
error_type(vm),
Expand Down
Loading