diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 380faf7ca..444502835 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -141,7 +141,7 @@ class TestFcntl(unittest.TestCase): finally: os.close(fd) - # TODO: RUSTPYTHON, AttributeError: module 'fcntl' has no attribute 'flock' + # TODO: RUSTPYTHON, TypeError: 'BufferedRandom' object cannot be interpreted as an integer @unittest.expectedFailure def test_flock(self): # Solaris needs readable file for shared lock @@ -157,7 +157,7 @@ class TestFcntl(unittest.TestCase): self.assertRaises(ValueError, fcntl.flock, -1, fcntl.LOCK_SH) self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH) - # TODO: RUSTPYTHON, AttributeError: module 'fcntl' has no attribute 'lockf' + # TODO: RUSTPYTHON, TypeError: 'BufferedRandom' object cannot be interpreted as an integer @unittest.expectedFailure @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") def test_lockf_exclusive(self): @@ -170,7 +170,7 @@ class TestFcntl(unittest.TestCase): fcntl.lockf(self.f, fcntl.LOCK_UN) self.assertEqual(p.exitcode, 0) - # TODO: RUSTPYTHON, AttributeError: module 'fcntl' has no attribute 'lockf' + # TODO: RUSTPYTHON, TypeError: 'BufferedRandom' object cannot be interpreted as an integer @unittest.expectedFailure @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") def test_lockf_share(self): diff --git a/stdlib/src/fcntl.rs b/stdlib/src/fcntl.rs index 43f170c58..502071a09 100644 --- a/stdlib/src/fcntl.rs +++ b/stdlib/src/fcntl.rs @@ -139,4 +139,74 @@ mod fcntl { } } } + + // XXX: at the time of writing, wasi and redox don't have the necessary constants/function + #[cfg(not(any(target_os = "wasi", target_os = "redox")))] + #[pyfunction] + fn flock(fd: i32, operation: i32, vm: &VirtualMachine) -> PyResult { + let ret = unsafe { libc::flock(fd, operation) }; + // TODO: add support for platforms that don't have a builtin `flock` syscall + if ret < 0 { + return Err(os::errno_err(vm)); + } + Ok(vm.ctx.new_int(ret).into()) + } + + // XXX: at the time of writing, wasi and redox don't have the necessary constants + #[cfg(not(any(target_os = "wasi", target_os = "redox")))] + #[pyfunction] + fn lockf( + fd: i32, + cmd: i32, + len: OptionalArg, + start: OptionalArg, + whence: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + let mut l: libc::flock = unsafe { std::mem::zeroed() }; + if cmd == libc::LOCK_UN { + l.l_type = libc::F_UNLCK + .try_into() + .map_err(|e| vm.new_overflow_error(format!("{e}")))?; + } else if (cmd & libc::LOCK_SH) != 0 { + l.l_type = libc::F_RDLCK + .try_into() + .map_err(|e| vm.new_overflow_error(format!("{e}")))?; + } else if (cmd & libc::LOCK_EX) != 0 { + l.l_type = libc::F_WRLCK + .try_into() + .map_err(|e| vm.new_overflow_error(format!("{e}")))?; + } else { + return Err(vm.new_value_error("unrecognized lockf argument".to_owned())); + } + l.l_start = match start { + OptionalArg::Present(s) => s.try_to_primitive(vm)?, + OptionalArg::Missing => 0, + }; + l.l_len = match len { + OptionalArg::Present(l_) => l_.try_to_primitive(vm)?, + OptionalArg::Missing => 0, + }; + l.l_whence = match whence { + OptionalArg::Present(w) => w + .try_into() + .map_err(|e| vm.new_overflow_error(format!("{e}")))?, + OptionalArg::Missing => 0, + }; + let ret = unsafe { + libc::fcntl( + fd, + if (cmd & libc::LOCK_NB) != 0 { + libc::F_SETLK + } else { + libc::F_SETLKW + }, + &l, + ) + }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + Ok(vm.ctx.new_int(ret).into()) + } }