mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Add _locale module (#4558)
_locale.localeconv and _locale.setlocale Co-authored-by: Jeong YunWon <jeong@youknowone.org>
This commit is contained in:
16
Lib/test/test_locale.py
vendored
16
Lib/test/test_locale.py
vendored
@@ -381,6 +381,8 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
|
||||
is_emscripten or is_wasi,
|
||||
"musl libc issue on Emscripten/WASI, bpo-46390"
|
||||
)
|
||||
# TODO: RUSTPYTHON", strcoll has not been implemented
|
||||
@unittest.expectedFailure
|
||||
def test_strcoll_with_diacritic(self):
|
||||
self.assertLess(locale.strcoll('à', 'b'), 0)
|
||||
|
||||
@@ -390,6 +392,8 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
|
||||
is_emscripten or is_wasi,
|
||||
"musl libc issue on Emscripten/WASI, bpo-46390"
|
||||
)
|
||||
# TODO: RUSTPYTHON", strxfrm has not been implemented
|
||||
@unittest.expectedFailure
|
||||
def test_strxfrm_with_diacritic(self):
|
||||
self.assertLess(locale.strxfrm('à'), locale.strxfrm('b'))
|
||||
|
||||
@@ -506,8 +510,6 @@ class NormalizeTest(unittest.TestCase):
|
||||
|
||||
|
||||
class TestMiscellaneous(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_defaults_UTF8(self):
|
||||
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
|
||||
# valid. Furthermore LC_CTYPE=UTF is used by the UTF-8 locale coercing
|
||||
@@ -544,6 +546,10 @@ class TestMiscellaneous(unittest.TestCase):
|
||||
|
||||
if orig_getlocale is not None:
|
||||
_locale._getdefaultlocale = orig_getlocale
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
if sys.platform == "win32":
|
||||
test_defaults_UTF8 = unittest.expectedFailure(test_defaults_UTF8)
|
||||
|
||||
def test_getencoding(self):
|
||||
# Invoke getencoding to make sure it does not cause exceptions.
|
||||
@@ -565,8 +571,6 @@ class TestMiscellaneous(unittest.TestCase):
|
||||
self.assertRaises(TypeError, locale.strcoll, "a", None)
|
||||
self.assertRaises(TypeError, locale.strcoll, b"a", None)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_setlocale_category(self):
|
||||
locale.setlocale(locale.LC_ALL)
|
||||
locale.setlocale(locale.LC_TIME)
|
||||
@@ -577,6 +581,10 @@ class TestMiscellaneous(unittest.TestCase):
|
||||
|
||||
# crasher from bug #7419
|
||||
self.assertRaises(locale.Error, locale.setlocale, 12345)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
if sys.platform == "win32":
|
||||
test_setlocale_category = unittest.expectedFailure(test_setlocale_category)
|
||||
|
||||
def test_getsetlocale_issue1813(self):
|
||||
# Issue #1813: setting and getting the locale under a Turkish locale
|
||||
|
||||
1
Lib/test/test_types.py
vendored
1
Lib/test/test_types.py
vendored
@@ -403,6 +403,7 @@ class TypesTests(unittest.TestCase):
|
||||
self.assertEqual(locale.format_string('%g', x, grouping=True), format(x, 'n'))
|
||||
self.assertEqual(locale.format_string('%.10g', x, grouping=True), format(x, '.10n'))
|
||||
|
||||
@unittest.skip("TODO: RustPython format code n is not integrated with locale")
|
||||
@run_with_locale('LC_NUMERIC', 'en_US.UTF8')
|
||||
def test_int__format__locale(self):
|
||||
# test locale support for __format__ code 'n' for integers
|
||||
|
||||
@@ -15,6 +15,7 @@ mod dis;
|
||||
mod gc;
|
||||
mod hashlib;
|
||||
mod json;
|
||||
mod locale;
|
||||
mod math;
|
||||
#[cfg(unix)]
|
||||
mod mmap;
|
||||
@@ -159,5 +160,9 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
|
||||
{
|
||||
"_uuid" => uuid::make_module,
|
||||
}
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
|
||||
{
|
||||
"_locale" => locale::make_module,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
151
stdlib/src/locale.rs
Normal file
151
stdlib/src/locale.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
|
||||
pub(crate) use _locale::make_module;
|
||||
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
|
||||
#[pymodule]
|
||||
mod _locale {
|
||||
use rustpython_vm::{
|
||||
builtins::{PyDictRef, PyIntRef, PyListRef, PyStrRef, PyTypeRef},
|
||||
convert::ToPyException,
|
||||
function::OptionalArg,
|
||||
PyObjectRef, PyResult, VirtualMachine,
|
||||
};
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
ptr,
|
||||
};
|
||||
|
||||
#[pyattr]
|
||||
use libc::{
|
||||
ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABMON_1, ABMON_10, ABMON_11,
|
||||
ABMON_12, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9,
|
||||
ALT_DIGITS, AM_STR, CODESET, CRNCYSTR, DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7,
|
||||
D_FMT, D_T_FMT, ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, LC_ALL, LC_COLLATE, LC_CTYPE,
|
||||
LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME, MON_1, MON_10, MON_11, MON_12, MON_2, MON_3,
|
||||
MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, NOEXPR, PM_STR, RADIXCHAR, THOUSEP, T_FMT,
|
||||
T_FMT_AMPM, YESEXPR,
|
||||
};
|
||||
|
||||
#[pyattr(name = "CHAR_MAX")]
|
||||
fn char_max(vm: &VirtualMachine) -> PyIntRef {
|
||||
vm.ctx.new_int(libc::c_char::MAX)
|
||||
}
|
||||
|
||||
unsafe fn copy_grouping(group: *const libc::c_char, vm: &VirtualMachine) -> PyListRef {
|
||||
let mut group_vec: Vec<PyObjectRef> = Vec::new();
|
||||
if group.is_null() {
|
||||
return vm.ctx.new_list(group_vec);
|
||||
}
|
||||
|
||||
let mut ptr = group;
|
||||
while ![0_i8, libc::c_char::MAX].contains(&*ptr) {
|
||||
let val = vm.ctx.new_int(*ptr);
|
||||
group_vec.push(val.into());
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
// https://github.com/python/cpython/blob/677320348728ce058fa3579017e985af74a236d4/Modules/_localemodule.c#L80
|
||||
if !group_vec.is_empty() {
|
||||
group_vec.push(vm.ctx.new_int(0).into());
|
||||
}
|
||||
vm.ctx.new_list(group_vec)
|
||||
}
|
||||
|
||||
unsafe fn pystr_from_raw_cstr(vm: &VirtualMachine, raw_ptr: *const libc::c_char) -> PyResult {
|
||||
let slice = unsafe { CStr::from_ptr(raw_ptr) };
|
||||
let string = slice
|
||||
.to_str()
|
||||
.expect("localeconv always return decodable string");
|
||||
Ok(vm.new_pyobj(string))
|
||||
}
|
||||
|
||||
#[pyattr(name = "Error", once)]
|
||||
fn error(vm: &VirtualMachine) -> PyTypeRef {
|
||||
vm.ctx.new_exception_type(
|
||||
"locale",
|
||||
"Error",
|
||||
Some(vec![vm.ctx.exceptions.exception_type.to_owned()]),
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn localeconv(vm: &VirtualMachine) -> PyResult<PyDictRef> {
|
||||
let result = vm.ctx.new_dict();
|
||||
|
||||
unsafe {
|
||||
let lc = libc::localeconv();
|
||||
|
||||
macro_rules! set_string_field {
|
||||
($field:ident) => {{
|
||||
result.set_item(
|
||||
stringify!($field),
|
||||
pystr_from_raw_cstr(vm, (*lc).$field)?,
|
||||
vm,
|
||||
)?
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! set_int_field {
|
||||
($field:ident) => {{
|
||||
result.set_item(stringify!($field), vm.new_pyobj((*lc).$field), vm)?
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! set_group_field {
|
||||
($field:ident) => {{
|
||||
result.set_item(
|
||||
stringify!($field),
|
||||
copy_grouping((*lc).$field, vm).into(),
|
||||
vm,
|
||||
)?
|
||||
}};
|
||||
}
|
||||
|
||||
set_group_field!(mon_grouping);
|
||||
set_group_field!(grouping);
|
||||
set_int_field!(int_frac_digits);
|
||||
set_int_field!(frac_digits);
|
||||
set_int_field!(p_cs_precedes);
|
||||
set_int_field!(p_sep_by_space);
|
||||
set_int_field!(n_cs_precedes);
|
||||
set_int_field!(p_sign_posn);
|
||||
set_int_field!(n_sign_posn);
|
||||
set_string_field!(decimal_point);
|
||||
set_string_field!(thousands_sep);
|
||||
set_string_field!(int_curr_symbol);
|
||||
set_string_field!(currency_symbol);
|
||||
set_string_field!(mon_decimal_point);
|
||||
set_string_field!(mon_thousands_sep);
|
||||
set_int_field!(n_sep_by_space);
|
||||
set_string_field!(positive_sign);
|
||||
set_string_field!(negative_sign);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct LocaleArgs {
|
||||
#[pyarg(any)]
|
||||
category: i32,
|
||||
#[pyarg(any, optional)]
|
||||
locale: OptionalArg<Option<PyStrRef>>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn setlocale(args: LocaleArgs, vm: &VirtualMachine) -> PyResult {
|
||||
unsafe {
|
||||
let result = match args.locale.flatten() {
|
||||
None => libc::setlocale(args.category, ptr::null()),
|
||||
Some(locale) => {
|
||||
let c_locale: CString =
|
||||
CString::new(locale.as_str()).map_err(|e| e.to_pyexception(vm))?;
|
||||
libc::setlocale(args.category, c_locale.as_ptr())
|
||||
}
|
||||
};
|
||||
if result.is_null() {
|
||||
let error = error(vm);
|
||||
return Err(vm.new_exception_msg(error, String::from("unsupported locale setting")));
|
||||
}
|
||||
pystr_from_raw_cstr(vm, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user