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
8 changes: 0 additions & 8 deletions Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,6 @@ def test_dot_new_with_str_key(self):
with self.assertRaises(TypeError):
h = hmac.new("key", digestmod='sha256')

# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_withtext(self):
# Constructor call with text.
Expand All @@ -443,8 +441,6 @@ def test_withtext(self):
self.fail("Constructor call with text argument raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_with_bytearray(self):
try:
Expand All @@ -454,8 +450,6 @@ def test_with_bytearray(self):
self.fail("Constructor call with bytearray arguments raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_with_memoryview_msg(self):
try:
Expand All @@ -464,8 +458,6 @@ def test_with_memoryview_msg(self):
self.fail("Constructor call with memoryview msg raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_withmodule(self):
# Constructor call with text and digest module.
Expand Down
29 changes: 20 additions & 9 deletions crates/stdlib/src/hashlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ pub mod _hashlib {
use crate::common::lock::PyRwLock;
use crate::vm::{
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
builtins::{PyBytes, PyStrRef, PyTypeRef},
builtins::{PyBytes, PyStrRef, PyTypeRef, PyValueError},
class::StaticType,
convert::ToPyObject,
function::{ArgBytesLike, ArgStrOrBytesLike, FuncArgs, OptionalArg},
protocol::PyBuffer,
types::Representable,
types::{Constructor, Initializer, Representable},
};
use blake2::{Blake2b512, Blake2s256};
use digest::{DynDigest, core_api::BlockSizeUser};
Expand All @@ -22,6 +22,12 @@ pub mod _hashlib {
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};

#[pyattr]
#[pyexception(name = "UnsupportedDigestmodError", base = PyValueError, impl)]
#[derive(Debug)]
#[repr(transparent)]
pub struct UnsupportedDigestmodError(PyValueError);

#[derive(FromArgs, Debug)]
#[allow(unused)]
struct NewHashArgs {
Expand Down Expand Up @@ -338,16 +344,21 @@ pub mod _hashlib {
#[allow(unused)]
pub struct NewHMACHashArgs {
#[pyarg(positional)]
name: PyBuffer,
key: ArgBytesLike,
#[pyarg(any, optional)]
data: OptionalArg<ArgBytesLike>,
#[pyarg(named, default = true)]
digestmod: bool, // TODO: RUSTPYTHON support functions & name functions
msg: OptionalArg<ArgBytesLike>,
#[pyarg(named, optional)]
digestmod: OptionalArg<PyObjectRef>,
}

#[pyfunction]
fn hmac_new(_args: NewHMACHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
Err(vm.new_type_error("cannot create 'hmac' instances")) // TODO: RUSTPYTHON support hmac
fn hmac_new(args: NewHMACHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
// Raise UnsupportedDigestmodError so Python's hmac.py falls back to pure-Python implementation
let _ = args;
Err(vm.new_exception_msg(
UnsupportedDigestmodError::static_type().to_owned(),
"unsupported hash type".to_owned(),
))
}

pub trait ThreadSafeDynDigest: DynClone + DynDigest + Sync + Send {}
Expand Down
3 changes: 2 additions & 1 deletion crates/stdlib/src/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,9 +651,10 @@ mod mmap {

impl AsBuffer for PyMmap {
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
let readonly = matches!(zelf.access, AccessMode::Read);
let buf = PyBuffer::new(
zelf.to_owned().into(),
BufferDescriptor::simple(zelf.__len__(), true),
BufferDescriptor::simple(zelf.__len__(), readonly),
&BUFFER_METHODS,
);

Expand Down
106 changes: 106 additions & 0 deletions crates/vm/src/stdlib/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ pub(super) mod _os {
#[pyattr]
pub(crate) const X_OK: u8 = 1 << 0;

// ST_RDONLY and ST_NOSUID flags for statvfs
#[cfg(all(unix, not(target_os = "redox")))]
#[pyattr]
const ST_RDONLY: libc::c_ulong = libc::ST_RDONLY;

#[cfg(all(unix, not(target_os = "redox")))]
#[pyattr]
const ST_NOSUID: libc::c_ulong = libc::ST_NOSUID;

#[pyfunction]
fn close(fileno: crt_fd::Owned) -> io::Result<()> {
crt_fd::close(fileno)
Expand Down Expand Up @@ -1701,6 +1710,103 @@ pub(super) mod _os {
#[pyclass(with(PyStructSequence))]
impl PyUnameResult {}

// statvfs_result: Result from statvfs or fstatvfs.
// = statvfs_result_fields
#[cfg(all(unix, not(target_os = "redox")))]
#[derive(Debug)]
#[pystruct_sequence_data]
pub(crate) struct StatvfsResultData {
pub f_bsize: libc::c_ulong, // filesystem block size
pub f_frsize: libc::c_ulong, // fragment size
pub f_blocks: libc::fsblkcnt_t, // size of fs in f_frsize units
pub f_bfree: libc::fsblkcnt_t, // free blocks
pub f_bavail: libc::fsblkcnt_t, // free blocks for unprivileged users
pub f_files: libc::fsfilcnt_t, // inodes
pub f_ffree: libc::fsfilcnt_t, // free inodes
pub f_favail: libc::fsfilcnt_t, // free inodes for unprivileged users
pub f_flag: libc::c_ulong, // mount flags
pub f_namemax: libc::c_ulong, // maximum filename length
#[pystruct_sequence(skip)]
pub f_fsid: libc::c_ulong, // filesystem ID (not in tuple but accessible as attribute)
}

#[cfg(all(unix, not(target_os = "redox")))]
#[pyattr]
#[pystruct_sequence(name = "statvfs_result", module = "os", data = "StatvfsResultData")]
pub(crate) struct PyStatvfsResult;

#[cfg(all(unix, not(target_os = "redox")))]
#[pyclass(with(PyStructSequence))]
impl PyStatvfsResult {
#[pyslot]
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let seq: PyObjectRef = args.bind(vm)?;
crate::types::struct_sequence_new(cls, seq, vm)
}
}

#[cfg(all(unix, not(target_os = "redox")))]
impl StatvfsResultData {
fn from_statvfs(st: libc::statvfs) -> Self {
// f_fsid is a struct on some platforms (e.g., Linux fsid_t) and a scalar on others.
// We extract raw bytes and interpret as a native-endian integer.
// Note: The value may differ across architectures due to endianness.
let f_fsid = {
let ptr = std::ptr::addr_of!(st.f_fsid) as *const u8;
let size = std::mem::size_of_val(&st.f_fsid);
if size >= 8 {
let bytes = unsafe { std::slice::from_raw_parts(ptr, 8) };
u64::from_ne_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6],
bytes[7],
]) as libc::c_ulong
} else if size >= 4 {
let bytes = unsafe { std::slice::from_raw_parts(ptr, 4) };
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as libc::c_ulong
} else {
0
}
};

Self {
f_bsize: st.f_bsize,
f_frsize: st.f_frsize,
f_blocks: st.f_blocks,
f_bfree: st.f_bfree,
f_bavail: st.f_bavail,
f_files: st.f_files,
f_ffree: st.f_ffree,
f_favail: st.f_favail,
f_flag: st.f_flag,
f_namemax: st.f_namemax,
f_fsid,
}
}
}

/// Perform a statvfs system call on the given path.
#[cfg(all(unix, not(target_os = "redox")))]
#[pyfunction]
#[pyfunction(name = "fstatvfs")]
fn statvfs(path: OsPathOrFd<'_>, vm: &VirtualMachine) -> PyResult {
let mut st: libc::statvfs = unsafe { std::mem::zeroed() };
let ret = match &path {
OsPathOrFd::Path(p) => {
let cpath = p.clone().into_cstring(vm)?;
unsafe { libc::statvfs(cpath.as_ptr(), &mut st) }
}
OsPathOrFd::Fd(fd) => unsafe { libc::fstatvfs(fd.as_raw(), &mut st) },
};
if ret != 0 {
return Err(OSErrorBuilder::with_filename(
&io::Error::last_os_error(),
path,
vm,
));
}
Ok(StatvfsResultData::from_statvfs(st).to_pyobject(vm))
}

pub(super) fn support_funcs() -> Vec<SupportFunc> {
let mut supports = super::platform::module::support_funcs();
supports.extend(vec![
Expand Down
25 changes: 24 additions & 1 deletion crates/vm/src/stdlib/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub(crate) mod _thread {
#[derive(PyPayload)]
struct RLock {
mu: RawRMutex,
count: std::sync::atomic::AtomicUsize,
}

impl fmt::Debug for RLock {
Expand All @@ -204,6 +205,7 @@ pub(crate) mod _thread {
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Self {
mu: RawRMutex::INIT,
count: std::sync::atomic::AtomicUsize::new(0),
}
.into_ref_with_type(vm, cls)
.map(Into::into)
Expand All @@ -213,14 +215,25 @@ pub(crate) mod _thread {
#[pymethod(name = "acquire_lock")]
#[pymethod(name = "__enter__")]
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
acquire_lock_impl!(&self.mu, args, vm)
let result = acquire_lock_impl!(&self.mu, args, vm)?;
if result {
self.count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
Ok(result)
}
#[pymethod]
#[pymethod(name = "release_lock")]
fn release(&self, vm: &VirtualMachine) -> PyResult<()> {
if !self.mu.is_locked() {
return Err(vm.new_runtime_error("release unlocked lock"));
}
debug_assert!(
self.count.load(std::sync::atomic::Ordering::Relaxed) > 0,
"RLock count underflow"
);
self.count
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
unsafe { self.mu.unlock() };
Ok(())
}
Expand All @@ -232,6 +245,7 @@ pub(crate) mod _thread {
self.mu.unlock();
};
}
self.count.store(0, std::sync::atomic::Ordering::Relaxed);
let new_mut = RawRMutex::INIT;

let old_mutex: AtomicCell<&RawRMutex> = AtomicCell::new(&self.mu);
Expand All @@ -245,6 +259,15 @@ pub(crate) mod _thread {
self.mu.is_owned_by_current_thread()
}

#[pymethod]
fn _recursion_count(&self) -> usize {
if self.mu.is_owned_by_current_thread() {
self.count.load(std::sync::atomic::Ordering::Relaxed)
} else {
0
}
}

#[pymethod]
fn __exit__(&self, _args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
self.release(vm)
Expand Down
Loading