mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
host_env: renameat support for os.rename (#8118)
Python's `os.rename` supports renaming a path relative to a directory descriptor - essentially, `renameat`. The syscall and its Rustix wrapper does all of the work, so this patch mainly updates the Python signature to forward to the implementation. As of this patch, `os.rename` is still incorrect for Windows because it always replaces the destination file. I added a small note for future contributors (or myself, since I might tackle it) as a reminder of what's broken.
This commit is contained in:
1 parent
40197480e9
commit
697a441e70
2 files changed
+56
-8
No files matched your search
@@ -1,7 +1,6 @@
|
||||
// spell-checker:disable
|
||||
// TODO: we can move more os-specific bindings/interfaces from stdlib::{os, posix, nt} to here
|
||||
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
use crate::crt_fd;
|
||||
#[cfg(windows)]
|
||||
use crate::fs;
|
||||
@@ -10,6 +9,8 @@ use core::ffi::CStr;
|
||||
use core::str::Utf8Error;
|
||||
#[cfg(windows)]
|
||||
use core::time::Duration;
|
||||
#[cfg(unix)]
|
||||
use rustix::fd::AsFd;
|
||||
use std::{
|
||||
env,
|
||||
ffi::{OsStr, OsString},
|
||||
@@ -266,11 +267,39 @@ pub fn copy_file_range(
|
||||
rustix::fs::copy_file_range(src, offset_src, dst, offset_dst, count)
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn rename(
|
||||
from: impl AsRef<std::path::Path>,
|
||||
from_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
to: impl AsRef<std::path::Path>,
|
||||
to_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
) -> io::Result<()> {
|
||||
std::fs::rename(from, to)
|
||||
if from_fd.is_none() && to_fd.is_none() {
|
||||
// TODO: Rust's implementation always overwrites the file so ensure consistency between
|
||||
// operating systems. We need to use windows-sys directly to distinguish between
|
||||
// os.rename and os.replace.
|
||||
std::fs::rename(from, to)
|
||||
} else {
|
||||
core::hint::cold_path();
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"renameat is not available on this platform",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn rename(
|
||||
from: impl AsRef<std::path::Path>,
|
||||
from_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
to: impl AsRef<std::path::Path>,
|
||||
to_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
) -> io::Result<()> {
|
||||
let from = from.as_ref();
|
||||
let from_fd = from_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
|
||||
let to = to.as_ref();
|
||||
let to_fd = to_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
|
||||
rustix::fs::renameat(from_fd, from, to_fd, to).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
||||
@@ -189,6 +189,7 @@ pub(super) mod _os {
|
||||
const UTIME_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox")));
|
||||
pub(crate) const SYMLINK_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox")));
|
||||
pub(crate) const UNLINK_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox")));
|
||||
const RENAME_DIR_FD: bool = cfg!(unix);
|
||||
const RMDIR_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox")));
|
||||
const SCANDIR_FD: bool = cfg!(all(unix, not(target_os = "redox")));
|
||||
|
||||
@@ -1377,19 +1378,37 @@ pub(super) mod _os {
|
||||
FsPath::try_from_path_like(path, false, vm)
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct RenameArgs<'fd> {
|
||||
#[pyarg(positional)]
|
||||
src: PyObjectRef,
|
||||
#[pyarg(positional)]
|
||||
dst: PyObjectRef,
|
||||
#[pyarg(any, default)]
|
||||
src_dir_fd: OptionalArg<crt_fd::Borrowed<'fd>>,
|
||||
#[pyarg(any, default)]
|
||||
dst_dir_fd: OptionalArg<crt_fd::Borrowed<'fd>>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
#[pyfunction(name = "replace")]
|
||||
fn rename(src: PyObjectRef, dst: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn rename(args: RenameArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let src = PathConverter::new()
|
||||
.function("rename")
|
||||
.argument("src")
|
||||
.try_path(src, vm)?;
|
||||
.try_path(args.src, vm)?;
|
||||
let dst = PathConverter::new()
|
||||
.function("rename")
|
||||
.argument("dst")
|
||||
.try_path(dst, vm)?;
|
||||
.try_path(args.dst, vm)?;
|
||||
|
||||
crate::host_env::os::rename(&src.path, &dst.path).map_err(|err| {
|
||||
crate::host_env::os::rename(
|
||||
&src,
|
||||
args.src_dir_fd.into_option(),
|
||||
&dst,
|
||||
args.dst_dir_fd.into_option(),
|
||||
)
|
||||
.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));
|
||||
@@ -1932,8 +1951,8 @@ pub(super) mod _os {
|
||||
SupportFunc::new("readlink", Some(false), None, Some(false)),
|
||||
SupportFunc::new("remove", Some(false), Some(UNLINK_DIR_FD), Some(false)),
|
||||
SupportFunc::new("unlink", Some(false), Some(UNLINK_DIR_FD), Some(false)),
|
||||
SupportFunc::new("rename", Some(false), None, Some(false)),
|
||||
SupportFunc::new("replace", Some(false), None, Some(false)), // TODO: Fix replace
|
||||
SupportFunc::new("rename", Some(false), Some(RENAME_DIR_FD), Some(false)),
|
||||
SupportFunc::new("replace", Some(false), Some(RENAME_DIR_FD), Some(false)), // TODO: Fix replace
|
||||
SupportFunc::new("rmdir", Some(false), Some(RMDIR_DIR_FD), Some(false)),
|
||||
SupportFunc::new("scandir", Some(SCANDIR_FD), Some(false), Some(false)),
|
||||
SupportFunc::new("stat", Some(true), Some(STAT_DIR_FD), Some(true)),
|
||||
|
||||
Reference in new issue
Block a user