mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Feat: grp module (#3967)
This commit is contained in:
1 parent
33b420fd0b
commit
bb52ed774e
6 files changed
+227
-8
No files matched your search
Generated
+1
@@ -1750,6 +1750,7 @@ dependencies = [
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"page_size",
|
||||
"parking_lot",
|
||||
"paste",
|
||||
"puruspe",
|
||||
"rand",
|
||||
|
||||
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
"""Test script for the grp module."""
|
||||
|
||||
import unittest
|
||||
from test.support import import_helper
|
||||
|
||||
|
||||
grp = import_helper.import_module('grp')
|
||||
@unittest.skipUnless(hasattr(grp, 'getgrall'), 'Does not have getgrall()')
|
||||
class GroupDatabaseTestCase(unittest.TestCase):
|
||||
|
||||
def check_value(self, value):
|
||||
# check that a grp tuple has the entries and
|
||||
# attributes promised by the docs
|
||||
self.assertEqual(len(value), 4)
|
||||
self.assertEqual(value[0], value.gr_name)
|
||||
self.assertIsInstance(value.gr_name, str)
|
||||
self.assertEqual(value[1], value.gr_passwd)
|
||||
self.assertIsInstance(value.gr_passwd, str)
|
||||
self.assertEqual(value[2], value.gr_gid)
|
||||
self.assertIsInstance(value.gr_gid, int)
|
||||
self.assertEqual(value[3], value.gr_mem)
|
||||
self.assertIsInstance(value.gr_mem, list)
|
||||
|
||||
def test_values(self):
|
||||
entries = grp.getgrall()
|
||||
|
||||
for e in entries:
|
||||
self.check_value(e)
|
||||
|
||||
def test_values_extended(self):
|
||||
entries = grp.getgrall()
|
||||
if len(entries) > 1000: # Huge group file (NIS?) -- skip the rest
|
||||
self.skipTest('huge group file, extended test skipped')
|
||||
|
||||
for e in entries:
|
||||
e2 = grp.getgrgid(e.gr_gid)
|
||||
self.check_value(e2)
|
||||
self.assertEqual(e2.gr_gid, e.gr_gid)
|
||||
name = e.gr_name
|
||||
if name.startswith('+') or name.startswith('-'):
|
||||
# NIS-related entry
|
||||
continue
|
||||
e2 = grp.getgrnam(name)
|
||||
self.check_value(e2)
|
||||
# There are instances where getgrall() returns group names in
|
||||
# lowercase while getgrgid() returns proper casing.
|
||||
# Discovered on Ubuntu 5.04 (custom).
|
||||
self.assertEqual(e2.gr_name.lower(), name.lower())
|
||||
|
||||
def test_errors(self):
|
||||
self.assertRaises(TypeError, grp.getgrgid)
|
||||
self.assertRaises(TypeError, grp.getgrnam)
|
||||
self.assertRaises(TypeError, grp.getgrall, 42)
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, grp.getgrnam, 'a\x00b')
|
||||
|
||||
# try to get some errors
|
||||
bynames = {}
|
||||
bygids = {}
|
||||
for (n, p, g, mem) in grp.getgrall():
|
||||
if not n or n == '+':
|
||||
continue # skip NIS entries etc.
|
||||
bynames[n] = g
|
||||
bygids[g] = n
|
||||
|
||||
allnames = list(bynames.keys())
|
||||
namei = 0
|
||||
fakename = allnames[namei]
|
||||
while fakename in bynames:
|
||||
chars = list(fakename)
|
||||
for i in range(len(chars)):
|
||||
if chars[i] == 'z':
|
||||
chars[i] = 'A'
|
||||
break
|
||||
elif chars[i] == 'Z':
|
||||
continue
|
||||
else:
|
||||
chars[i] = chr(ord(chars[i]) + 1)
|
||||
break
|
||||
else:
|
||||
namei = namei + 1
|
||||
try:
|
||||
fakename = allnames[namei]
|
||||
except IndexError:
|
||||
# should never happen... if so, just forget it
|
||||
break
|
||||
fakename = ''.join(chars)
|
||||
|
||||
self.assertRaises(KeyError, grp.getgrnam, fakename)
|
||||
|
||||
# Choose a non-existent gid.
|
||||
fakegid = 4127
|
||||
while fakegid in bygids:
|
||||
fakegid = (fakegid * 3) % 0x10000
|
||||
|
||||
self.assertRaises(KeyError, grp.getgrgid, fakegid)
|
||||
|
||||
def test_noninteger_gid(self):
|
||||
entries = grp.getgrall()
|
||||
if not entries:
|
||||
self.skipTest('no groups')
|
||||
# Choose an existent gid.
|
||||
gid = entries[0][2]
|
||||
self.assertRaises(TypeError, grp.getgrgid, float(gid))
|
||||
self.assertRaises(TypeError, grp.getgrgid, str(gid))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Vendored
+4
@@ -1999,6 +1999,8 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
subprocess.check_call(ZERO_RETURN_CMD, user=65535)
|
||||
|
||||
# TODO: RUSTPYTHON, observed gids do not match expected gids
|
||||
@unittest.expectedFailure
|
||||
@unittest.skipUnless(hasattr(os, 'setregid'), 'no setregid() on platform')
|
||||
def test_group(self):
|
||||
gid = os.getegid()
|
||||
@@ -2046,6 +2048,8 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
subprocess.check_call(ZERO_RETURN_CMD, group=65535)
|
||||
|
||||
# TODO: RUSTPYTHON, observed gids do not match expected gids
|
||||
@unittest.expectedFailure
|
||||
@unittest.skipUnless(hasattr(os, 'setgroups'), 'no setgroups() on platform')
|
||||
def test_extra_groups(self):
|
||||
gid = os.getegid()
|
||||
|
||||
@@ -63,6 +63,7 @@ ahash = "0.7.6"
|
||||
libz-sys = { version = "1.1.5", optional = true }
|
||||
num_enum = "0.5.7"
|
||||
ascii = "1.0.0"
|
||||
parking_lot = "0.12.0"
|
||||
|
||||
# mmap
|
||||
[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies]
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
pub(crate) use grp::make_module;
|
||||
|
||||
#[pymodule]
|
||||
mod grp {
|
||||
use crate::vm::{
|
||||
builtins::{PyIntRef, PyListRef, PyStrRef},
|
||||
convert::{IntoPyException, ToPyObject},
|
||||
exceptions,
|
||||
types::PyStructSequence,
|
||||
PyObjectRef, PyResult, VirtualMachine,
|
||||
};
|
||||
use nix::unistd;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "grp", name = "struct_group")]
|
||||
#[derive(PyStructSequence)]
|
||||
struct Group {
|
||||
gr_name: String,
|
||||
gr_passwd: String,
|
||||
gr_gid: u32,
|
||||
gr_mem: PyListRef,
|
||||
}
|
||||
#[pyimpl(with(PyStructSequence))]
|
||||
impl Group {
|
||||
fn from_unistd_group(group: unistd::Group, vm: &VirtualMachine) -> Self {
|
||||
let cstr_lossy = |s: std::ffi::CString| {
|
||||
s.into_string()
|
||||
.unwrap_or_else(|e| e.into_cstring().to_string_lossy().into_owned())
|
||||
};
|
||||
Group {
|
||||
gr_name: group.name,
|
||||
gr_passwd: cstr_lossy(group.passwd),
|
||||
gr_gid: group.gid.as_raw(),
|
||||
gr_mem: vm
|
||||
.ctx
|
||||
.new_list(group.mem.iter().map(|s| s.to_pyobject(vm)).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn getgrgid(gid: PyIntRef, vm: &VirtualMachine) -> PyResult<Group> {
|
||||
let gr_gid = gid.as_bigint();
|
||||
let gid = libc::gid_t::try_from(gr_gid)
|
||||
.map(unistd::Gid::from_raw)
|
||||
.ok();
|
||||
let group = gid
|
||||
.map(unistd::Group::from_gid)
|
||||
.transpose()
|
||||
.map_err(|err| err.into_pyexception(vm))?
|
||||
.flatten();
|
||||
let group = group.ok_or_else(|| {
|
||||
vm.new_key_error(
|
||||
vm.ctx
|
||||
.new_str(format!("getgrgid: group id {} not found", gr_gid))
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
Ok(Group::from_unistd_group(group, vm))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn getgrnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult<Group> {
|
||||
let gr_name = name.as_str();
|
||||
if gr_name.contains('\0') {
|
||||
return Err(exceptions::cstring_error(vm));
|
||||
}
|
||||
let group = unistd::Group::from_name(gr_name).map_err(|err| err.into_pyexception(vm))?;
|
||||
let group = group.ok_or_else(|| {
|
||||
vm.new_key_error(
|
||||
vm.ctx
|
||||
.new_str(format!("getgrnam: group name {} not found", gr_name))
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
Ok(Group::from_unistd_group(group, vm))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn getgrall(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
// setgrent, getgrent, etc are not thread safe. Could use fgetgrent_r, but this is easier
|
||||
static GETGRALL: parking_lot::Mutex<()> = parking_lot::const_mutex(());
|
||||
let _guard = GETGRALL.lock();
|
||||
let mut list = Vec::new();
|
||||
|
||||
unsafe { libc::setgrent() };
|
||||
while let Some(ptr) = NonNull::new(unsafe { libc::getgrent() }) {
|
||||
let group = unistd::Group::from(unsafe { ptr.as_ref() });
|
||||
let group = Group::from_unistd_group(group, vm).to_pyobject(vm);
|
||||
list.push(group);
|
||||
}
|
||||
unsafe { libc::endgrent() };
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
+15
-8
@@ -41,6 +41,8 @@ mod multiprocessing;
|
||||
#[cfg(unix)]
|
||||
mod posixsubprocess;
|
||||
// libc is missing constants on redox
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
|
||||
mod grp;
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
mod resource;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -118,20 +120,25 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
|
||||
{
|
||||
"_ssl" => ssl::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "ios")))]
|
||||
{
|
||||
"termios" => termios::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
{
|
||||
"resource" => resource::make_module,
|
||||
}
|
||||
// Unix-only
|
||||
#[cfg(unix)]
|
||||
{
|
||||
"_posixsubprocess" => posixsubprocess::make_module,
|
||||
"syslog" => syslog::make_module,
|
||||
"mmap" => mmap::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
{
|
||||
"resource" => resource::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
|
||||
{
|
||||
"termios" => termios::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
|
||||
{
|
||||
"grp" => grp::make_module,
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
"_scproxy" => scproxy::make_module,
|
||||
|
||||
Reference in new issue
Block a user