diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index f88659764bc..653a05dd011 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2350,7 +2350,6 @@ def check_bool(self, f, *args, **kwargs): with self.assertRaises(RuntimeWarning): f(fd, *args, **kwargs) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_fdopen(self): self.check(os.fdopen, encoding="utf-8") self.check_bool(os.fdopen, encoding="utf-8") diff --git a/crates/stdlib/src/termios.rs b/crates/stdlib/src/termios.rs index 48dfae25a73..de402724434 100644 --- a/crates/stdlib/src/termios.rs +++ b/crates/stdlib/src/termios.rs @@ -261,13 +261,12 @@ mod termios { } fn termios_error(err: std::io::Error, vm: &VirtualMachine) -> PyBaseExceptionRef { - vm.new_exception( + vm.new_os_subtype_error( error_type(vm), - vec![ - err.posix_errno().to_pyobject(vm), - vm.ctx.new_str(err.to_string()).into(), - ], + Some(err.posix_errno()), + vm.ctx.new_str(err.to_string()), ) + .upcast() } #[pyattr(name = "error", once)] diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 691cb068605..95836e38337 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -1268,13 +1268,64 @@ pub(super) mod _os { } #[pyfunction] - fn link(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> { - fs::hard_link(&src.path, &dst.path).map_err(|err| { - let builder = err.to_os_error_builder(vm); - let builder = builder.filename(src.filename(vm)); - let builder = builder.filename2(dst.filename(vm)); - builder.build(vm).upcast() - }) + fn link( + src: OsPath, + dst: OsPath, + follow_symlinks: FollowSymlinks, + vm: &VirtualMachine, + ) -> PyResult<()> { + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + let src_cstr = std::ffi::CString::new(src.path.as_os_str().as_bytes()) + .map_err(|_| vm.new_value_error("embedded null byte"))?; + let dst_cstr = std::ffi::CString::new(dst.path.as_os_str().as_bytes()) + .map_err(|_| vm.new_value_error("embedded null byte"))?; + + let flags = if follow_symlinks.0 { + libc::AT_SYMLINK_FOLLOW + } else { + 0 + }; + + let ret = unsafe { + libc::linkat( + libc::AT_FDCWD, + src_cstr.as_ptr(), + libc::AT_FDCWD, + dst_cstr.as_ptr(), + flags, + ) + }; + + if ret != 0 { + let err = std::io::Error::last_os_error(); + let builder = err.to_os_error_builder(vm); + let builder = builder.filename(src.filename(vm)); + let builder = builder.filename2(dst.filename(vm)); + return Err(builder.build(vm).upcast()); + } + + Ok(()) + } + + #[cfg(not(unix))] + { + // On non-Unix platforms, ignore follow_symlinks if it's the default value + // or raise NotImplementedError if explicitly set to False + if !follow_symlinks.0 { + return Err(vm.new_not_implemented_error( + "link: follow_symlinks unavailable on this platform", + )); + } + + fs::hard_link(&src.path, &dst.path).map_err(|err| { + let builder = err.to_os_error_builder(vm); + let builder = builder.filename(src.filename(vm)); + let builder = builder.filename2(dst.filename(vm)); + builder.build(vm).upcast() + }) + } } #[cfg(any(unix, windows))] @@ -1842,6 +1893,7 @@ pub(super) mod _os { SupportFunc::new("access", Some(false), Some(false), None), SupportFunc::new("chdir", None, Some(false), Some(false)), // chflags Some, None Some + SupportFunc::new("link", Some(false), Some(false), Some(cfg!(unix))), SupportFunc::new("listdir", Some(LISTDIR_FD), Some(false), Some(false)), SupportFunc::new("mkdir", Some(false), Some(MKDIR_DIR_FD), Some(false)), // mkfifo Some Some None diff --git a/crates/vm/src/stdlib/warnings.rs b/crates/vm/src/stdlib/warnings.rs index ad54b806220..198df07d6c0 100644 --- a/crates/vm/src/stdlib/warnings.rs +++ b/crates/vm/src/stdlib/warnings.rs @@ -12,7 +12,7 @@ pub fn warn( if let Ok(module) = vm.import("warnings", 0) && let Ok(func) = module.get_attr("warn", vm) { - let _ = func.call((message, category.to_owned(), stack_level), vm); + func.call((message, category.to_owned(), stack_level), vm)?; } Ok(()) } diff --git a/crates/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs index 3a534302c31..7b5720fdd84 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -328,6 +328,7 @@ impl VirtualMachine { Some(if write { "wb" } else { "rb" }), crate::stdlib::io::OpenArgs { buffering: if unbuffered { 0 } else { -1 }, + closefd: false, ..Default::default() }, self,