diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 9bbb313f8..0d67d3680 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2206,9 +2206,9 @@ mod _socket { Some(t) => { let f = t.into_float(); if f.is_nan() { - return Err(vm.new_value_error( - "Invalid value NaN (not a number)".to_owned(), - )); + return Err( + vm.new_value_error("Invalid value NaN (not a number)".to_owned()) + ); } if f < 0.0 || !f.is_finite() { return Err(vm.new_value_error("Timeout value out of range".to_owned())); @@ -3388,9 +3388,7 @@ mod _socket { Some(t) => { let f = t.into_float(); if f.is_nan() { - return Err(vm.new_value_error( - "Invalid value NaN (not a number)".to_owned(), - )); + return Err(vm.new_value_error("Invalid value NaN (not a number)".to_owned())); } if f < 0.0 || !f.is_finite() { return Err(vm.new_value_error("Timeout value out of range".to_owned())); diff --git a/crates/vm/src/convert/try_from.rs b/crates/vm/src/convert/try_from.rs index f6e917a3d..b8d1b53e2 100644 --- a/crates/vm/src/convert/try_from.rs +++ b/crates/vm/src/convert/try_from.rs @@ -127,9 +127,7 @@ impl TryFromObject for core::time::Duration { if let Some(float) = obj.downcast_ref::() { let f = float.to_f64(); if f.is_nan() { - return Err( - vm.new_value_error("Invalid value NaN (not a number)".to_owned()) - ); + return Err(vm.new_value_error("Invalid value NaN (not a number)".to_owned())); } if f < 0.0 { return Err(vm.new_value_error("negative duration".to_owned())); diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 82a5532ab..6af6e6222 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -15,11 +15,20 @@ pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()> Ok(()) } -#[pymodule(name = "posix", with(super::os::_os))] +#[pymodule(name = "posix", with( + super::os::_os, + #[cfg(any( + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd", + target_os = "android" + ))] + posix_sched +))] pub mod module { use crate::{ - AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, - builtins::{PyDictRef, PyInt, PyListRef, PyStr, PyTupleRef, PyType}, + AsObject, Py, PyObjectRef, PyResult, VirtualMachine, + builtins::{PyDictRef, PyInt, PyListRef, PyStr, PyTupleRef}, convert::{IntoPyException, ToPyObject, TryFromObject}, exceptions::OSErrorBuilder, function::{Either, KwArgs, OptionalArg}, @@ -28,7 +37,6 @@ pub mod module { _os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata, warn_if_bool_fd, }, - types::{Constructor, Representable}, }; #[cfg(any( target_os = "android", @@ -889,206 +897,6 @@ pub mod module { nix::sched::sched_yield().map_err(|e| e.into_pyexception(vm)) } - #[pyattr] - #[pyclass(name = "sched_param")] - #[derive(Debug, PyPayload)] - struct SchedParam { - sched_priority: PyObjectRef, - } - - impl TryFromObject for SchedParam { - fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - Ok(Self { - sched_priority: obj, - }) - } - } - - #[pyclass(with(Constructor, Representable))] - impl SchedParam { - #[pygetset] - fn sched_priority(&self, vm: &VirtualMachine) -> PyObjectRef { - self.sched_priority.clone().to_pyobject(vm) - } - - #[pymethod] - fn __reduce__(zelf: crate::PyRef, vm: &VirtualMachine) -> PyTupleRef { - vm.new_tuple((zelf.class().to_owned(), (zelf.sched_priority.clone(),))) - } - - #[pymethod] - fn __replace__( - zelf: crate::PyRef, - args: crate::function::FuncArgs, - vm: &VirtualMachine, - ) -> PyResult { - if !args.args.is_empty() { - return Err( - vm.new_type_error("__replace__() takes no positional arguments".to_owned()) - ); - } - let sched_priority = match args.kwargs.get("sched_priority") { - Some(v) => v.clone(), - None => zelf.sched_priority.clone(), - }; - // Check for unexpected keyword arguments - for key in args.kwargs.keys() { - if key.as_str() != "sched_priority" { - return Err(vm.new_type_error(format!( - "__replace__() got an unexpected keyword argument '{key}'" - ))); - } - } - Ok(Self { sched_priority }) - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[cfg(not(target_env = "musl"))] - fn try_to_libc(&self, vm: &VirtualMachine) -> PyResult { - use crate::AsObject; - let priority_class = self.sched_priority.class(); - let priority_type = priority_class.name(); - let priority = self.sched_priority.clone(); - let value = priority.downcast::().map_err(|_| { - vm.new_type_error(format!("an integer is required (got type {priority_type})")) - })?; - let sched_priority = value.try_to_primitive(vm)?; - Ok(libc::sched_param { sched_priority }) - } - } - - #[derive(FromArgs)] - pub struct SchedParamArg { - sched_priority: PyObjectRef, - } - - impl Constructor for SchedParam { - type Args = SchedParamArg; - - fn py_new(_cls: &Py, arg: Self::Args, _vm: &VirtualMachine) -> PyResult { - Ok(Self { - sched_priority: arg.sched_priority, - }) - } - } - - impl Representable for SchedParam { - #[inline] - fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { - let sched_priority_repr = zelf.sched_priority.repr(vm)?; - Ok(format!( - "posix.sched_param(sched_priority = {})", - sched_priority_repr.as_str() - )) - } - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[pyfunction] - fn sched_getscheduler(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult { - let policy = unsafe { libc::sched_getscheduler(pid) }; - if policy == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(policy) - } - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[derive(FromArgs)] - struct SchedSetschedulerArgs { - #[pyarg(positional)] - pid: i32, - #[pyarg(positional)] - policy: i32, - #[pyarg(positional)] - sched_param_obj: crate::PyRef, - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[cfg(not(target_env = "musl"))] - #[pyfunction] - fn sched_setscheduler(args: SchedSetschedulerArgs, vm: &VirtualMachine) -> PyResult { - let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?; - let policy = unsafe { libc::sched_setscheduler(args.pid, args.policy, &libc_sched_param) }; - if policy == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(policy) - } - } - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[pyfunction] - fn sched_getparam(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult { - let param = unsafe { - let mut param = core::mem::MaybeUninit::uninit(); - if -1 == libc::sched_getparam(pid, param.as_mut_ptr()) { - return Err(vm.new_last_errno_error()); - } - param.assume_init() - }; - Ok(SchedParam { - sched_priority: param.sched_priority.to_pyobject(vm), - }) - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[derive(FromArgs)] - struct SchedSetParamArgs { - #[pyarg(positional)] - pid: i32, - #[pyarg(positional)] - sched_param_obj: crate::PyRef, - } - - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - target_os = "android" - ))] - #[cfg(not(target_env = "musl"))] - #[pyfunction] - fn sched_setparam(args: SchedSetParamArgs, vm: &VirtualMachine) -> PyResult { - let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?; - let ret = unsafe { libc::sched_setparam(args.pid, &libc_sched_param) }; - if ret == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(ret) - } - } - #[pyfunction] fn get_inheritable(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult { let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD); @@ -2699,3 +2507,159 @@ pub mod module { Ok(()) } } + +#[cfg(any( + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd", + target_os = "android" +))] +#[pymodule(sub)] +mod posix_sched { + use crate::{ + AsObject, Py, PyObjectRef, PyResult, VirtualMachine, + builtins::{PyInt, PyTupleRef}, + convert::ToPyObject, + function::FuncArgs, + types::PyStructSequence, + }; + + #[derive(FromArgs)] + struct SchedParamArgs { + #[pyarg(any)] + sched_priority: PyObjectRef, + } + + #[pystruct_sequence_data] + struct SchedParamData { + pub sched_priority: PyObjectRef, + } + + #[pyattr] + #[pystruct_sequence(name = "sched_param", module = "posix", data = "SchedParamData")] + struct PySchedParam; + + #[pyclass(with(PyStructSequence))] + impl PySchedParam { + #[pyslot] + fn slot_new( + cls: crate::builtins::PyTypeRef, + args: FuncArgs, + vm: &VirtualMachine, + ) -> PyResult { + use crate::PyPayload; + let SchedParamArgs { sched_priority } = args.bind(vm)?; + let items = vec![sched_priority]; + crate::builtins::PyTuple::new_unchecked(items.into_boxed_slice()) + .into_ref_with_type(vm, cls) + .map(Into::into) + } + + #[extend_class] + fn extend_pyclass(ctx: &crate::vm::Context, class: &'static Py) { + // Override __reduce__ to return (type, (sched_priority,)) + // instead of the generic structseq (type, ((sched_priority,),)). + // The trait's extend_class checks contains_key before setting default. + const SCHED_PARAM_REDUCE: crate::function::PyMethodDef = + crate::function::PyMethodDef::new_const( + "__reduce__", + |zelf: crate::PyRef, + vm: &VirtualMachine| + -> PyTupleRef { + vm.new_tuple((zelf.class().to_owned(), (zelf[0].clone(),))) + }, + crate::function::PyMethodFlags::METHOD, + None, + ); + class.set_attr( + ctx.intern_str("__reduce__"), + SCHED_PARAM_REDUCE.to_proper_method(class, ctx), + ); + } + } + + #[cfg(not(target_env = "musl"))] + fn convert_sched_param(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + use crate::{builtins::PyTuple, class::StaticType}; + if !obj.fast_isinstance(PySchedParam::static_type()) { + return Err(vm.new_type_error("must have a sched_param object".to_owned())); + } + let tuple = obj.downcast_ref::().unwrap(); + let priority = tuple[0].clone(); + let priority_type = priority.class().name().to_string(); + let value = priority.downcast::().map_err(|_| { + vm.new_type_error(format!("an integer is required (got type {priority_type})")) + })?; + let sched_priority = value.try_to_primitive(vm)?; + Ok(libc::sched_param { sched_priority }) + } + + #[pyfunction] + fn sched_getscheduler(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult { + let policy = unsafe { libc::sched_getscheduler(pid) }; + if policy == -1 { + Err(vm.new_last_errno_error()) + } else { + Ok(policy) + } + } + + #[derive(FromArgs)] + struct SchedSetschedulerArgs { + #[pyarg(positional)] + pid: i32, + #[pyarg(positional)] + policy: i32, + #[pyarg(positional)] + sched_param: PyObjectRef, + } + + #[cfg(not(target_env = "musl"))] + #[pyfunction] + fn sched_setscheduler(args: SchedSetschedulerArgs, vm: &VirtualMachine) -> PyResult { + let libc_sched_param = convert_sched_param(&args.sched_param, vm)?; + let policy = unsafe { libc::sched_setscheduler(args.pid, args.policy, &libc_sched_param) }; + if policy == -1 { + Err(vm.new_last_errno_error()) + } else { + Ok(policy) + } + } + + #[pyfunction] + fn sched_getparam(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult { + let param = unsafe { + let mut param = core::mem::MaybeUninit::uninit(); + if -1 == libc::sched_getparam(pid, param.as_mut_ptr()) { + return Err(vm.new_last_errno_error()); + } + param.assume_init() + }; + Ok(PySchedParam::from_data( + SchedParamData { + sched_priority: param.sched_priority.to_pyobject(vm), + }, + vm, + )) + } + + #[derive(FromArgs)] + struct SchedSetParamArgs { + #[pyarg(positional)] + pid: i32, + #[pyarg(positional)] + sched_param: PyObjectRef, + } + + #[cfg(not(target_env = "musl"))] + #[pyfunction] + fn sched_setparam(args: SchedSetParamArgs, vm: &VirtualMachine) -> PyResult { + let libc_sched_param = convert_sched_param(&args.sched_param, vm)?; + let ret = unsafe { libc::sched_setparam(args.pid, &libc_sched_param) }; + if ret == -1 { + Err(vm.new_last_errno_error()) + } else { + Ok(ret) + } + } +}