Skip to content

Commit 19193cd

Browse files
minhrongcon2000youknowone
authored andcommitted
Add locale implementation for windows
1 parent c15f670 commit 19193cd

File tree

4 files changed

+114
-43
lines changed

4 files changed

+114
-43
lines changed

Lib/test/test_locale.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ def test_currency(self):
343343
class TestCollation(unittest.TestCase):
344344
# Test string collation functions
345345

346-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
347346
def test_strcoll(self):
348347
self.assertLess(locale.strcoll('a', 'b'), 0)
349348
self.assertEqual(locale.strcoll('a', 'a'), 0)
@@ -352,7 +351,6 @@ def test_strcoll(self):
352351
self.assertRaises(ValueError, locale.strcoll, 'a\0', 'a')
353352
self.assertRaises(ValueError, locale.strcoll, 'a', 'a\0')
354353

355-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
356354
def test_strxfrm(self):
357355
self.assertLess(locale.strxfrm('a'), locale.strxfrm('b'))
358356
# embedded null character
@@ -504,7 +502,6 @@ def test_japanese(self):
504502

505503

506504
class TestMiscellaneous(unittest.TestCase):
507-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
508505
def test_defaults_UTF8(self):
509506
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
510507
# valid. Furthermore LC_CTYPE=UTF is used by the UTF-8 locale coercing
@@ -562,7 +559,6 @@ def test_strcoll_3303(self):
562559
self.assertRaises(TypeError, locale.strcoll, "a", None)
563560
self.assertRaises(TypeError, locale.strcoll, b"a", None)
564561

565-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
566562
def test_setlocale_category(self):
567563
locale.setlocale(locale.LC_ALL)
568564
locale.setlocale(locale.LC_TIME)

stdlib/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,4 @@ features = [
115115
]
116116

117117
[target.'cfg(target_os = "macos")'.dependencies]
118-
system-configuration = "0.5.0"
118+
system-configuration = "0.5.0"

stdlib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
160160
{
161161
"_uuid" => uuid::make_module,
162162
}
163-
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
163+
#[cfg(any(unix, windows, not(any(target_os = "ios", target_os = "android", target_arch="wasm32"))))]
164164
{
165165
"_locale" => locale::make_module,
166166
}

stdlib/src/locale.rs

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,49 @@
1-
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
1+
#[cfg(any(
2+
unix,
3+
windows,
4+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
5+
))]
26
pub(crate) use _locale::make_module;
37

4-
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
8+
#[cfg(not(target_arch = "wasm32"))]
9+
#[repr(C)]
10+
struct lconv {
11+
decimal_point: *mut libc::c_char,
12+
thousands_sep: *mut libc::c_char,
13+
grouping: *mut libc::c_char,
14+
int_curr_symbol: *mut libc::c_char,
15+
currency_symbol: *mut libc::c_char,
16+
mon_decimal_point: *mut libc::c_char,
17+
mon_thousands_sep: *mut libc::c_char,
18+
mon_grouping: *mut libc::c_char,
19+
positive_sign: *mut libc::c_char,
20+
negative_sign: *mut libc::c_char,
21+
int_frac_digits: libc::c_char,
22+
frac_digits: libc::c_char,
23+
p_cs_precedes: libc::c_char,
24+
p_sep_by_space: libc::c_char,
25+
n_cs_precedes: libc::c_char,
26+
n_sep_by_space: libc::c_char,
27+
p_sign_posn: libc::c_char,
28+
n_sign_posn: libc::c_char,
29+
int_p_cs_precedes: libc::c_char,
30+
int_n_cs_precedes: libc::c_char,
31+
int_p_sep_by_space: libc::c_char,
32+
int_n_sep_by_space: libc::c_char,
33+
int_p_sign_posn: libc::c_char,
34+
int_n_sign_posn: libc::c_char,
35+
}
36+
37+
#[cfg(not(target_arch = "wasm32"))]
38+
extern "C" {
39+
fn localeconv() -> *mut lconv;
40+
}
41+
42+
#[cfg(any(
43+
unix,
44+
windows,
45+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
46+
))]
547
#[pymodule]
648
mod _locale {
749
use rustpython_vm::{
@@ -15,17 +57,25 @@ mod _locale {
1557
ptr,
1658
};
1759

60+
#[cfg(all(unix, not(any(target_os = "ios", target_os = "android"))))]
1861
#[pyattr]
1962
use libc::{
2063
ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABMON_1, ABMON_10, ABMON_11,
2164
ABMON_12, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9,
2265
ALT_DIGITS, AM_STR, CODESET, CRNCYSTR, DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7,
23-
D_FMT, D_T_FMT, ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, LC_ALL, LC_COLLATE, LC_CTYPE,
24-
LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME, MON_1, MON_10, MON_11, MON_12, MON_2, MON_3,
25-
MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, NOEXPR, PM_STR, RADIXCHAR, THOUSEP, T_FMT,
26-
T_FMT_AMPM, YESEXPR,
66+
D_FMT, D_T_FMT, ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, LC_MESSAGES, MON_1, MON_10, MON_11,
67+
MON_12, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, NOEXPR, PM_STR, RADIXCHAR,
68+
THOUSEP, T_FMT, T_FMT_AMPM, YESEXPR,
2769
};
2870

71+
#[pyattr]
72+
use libc::{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME};
73+
74+
#[cfg(any(
75+
unix,
76+
windows,
77+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
78+
))]
2979
#[pyattr(name = "CHAR_MAX")]
3080
fn char_max(vm: &VirtualMachine) -> PyIntRef {
3181
vm.ctx.new_int(libc::c_char::MAX)
@@ -58,6 +108,11 @@ mod _locale {
58108
Ok(vm.new_pyobj(string))
59109
}
60110

111+
#[cfg(any(
112+
unix,
113+
windows,
114+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
115+
))]
61116
#[pyattr(name = "Error", once)]
62117
fn error(vm: &VirtualMachine) -> PyTypeRef {
63118
vm.ctx.new_exception_type(
@@ -67,76 +122,88 @@ mod _locale {
67122
)
68123
}
69124

125+
#[cfg(any(
126+
unix,
127+
windows,
128+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
129+
))]
70130
#[pyfunction]
71131
fn strcoll(string1: PyStrRef, string2: PyStrRef, vm: &VirtualMachine) -> PyResult {
72132
let cstr1 = CString::new(string1.as_str()).map_err(|e| e.to_pyexception(vm))?;
73133
let cstr2 = CString::new(string2.as_str()).map_err(|e| e.to_pyexception(vm))?;
74134
Ok(vm.new_pyobj(unsafe { libc::strcoll(cstr1.as_ptr(), cstr2.as_ptr()) }))
75135
}
76136

137+
#[cfg(any(
138+
unix,
139+
windows,
140+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
141+
))]
77142
#[pyfunction]
78143
fn strxfrm(string: PyStrRef, vm: &VirtualMachine) -> PyResult {
79144
// https://github.com/python/cpython/blob/eaae563b6878aa050b4ad406b67728b6b066220e/Modules/_localemodule.c#L390-L442
80145
let n1 = string.byte_len() + 1;
81-
let mut buff: Vec<u8> = vec![0; n1];
146+
let mut buff = vec![0u8; n1];
82147

83148
let cstr = CString::new(string.as_str()).map_err(|e| e.to_pyexception(vm))?;
84149
let n2 = unsafe { libc::strxfrm(buff.as_mut_ptr() as _, cstr.as_ptr(), n1) };
85-
buff.truncate(n2);
150+
buff = vec![0u8; n2 + 1];
151+
unsafe {
152+
libc::strxfrm(buff.as_mut_ptr() as _, cstr.as_ptr(), n2 + 1);
153+
}
86154
Ok(vm.new_pyobj(String::from_utf8(buff).expect("strxfrm returned invalid utf-8 string")))
87155
}
88156

89-
#[pyfunction]
90-
fn localeconv(vm: &VirtualMachine) -> PyResult<PyDictRef> {
157+
#[pyfunction(name = "localeconv")]
158+
fn _localeconv(vm: &VirtualMachine) -> PyResult<PyDictRef> {
91159
let result = vm.ctx.new_dict();
92160

93161
unsafe {
94-
let lc = libc::localeconv();
95-
96162
macro_rules! set_string_field {
97-
($field:ident) => {{
163+
($lc:expr, $field:ident) => {{
98164
result.set_item(
99165
stringify!($field),
100-
pystr_from_raw_cstr(vm, (*lc).$field)?,
166+
pystr_from_raw_cstr(vm, (*$lc).$field)?,
101167
vm,
102168
)?
103169
}};
104170
}
105171

106172
macro_rules! set_int_field {
107-
($field:ident) => {{
108-
result.set_item(stringify!($field), vm.new_pyobj((*lc).$field), vm)?
173+
($lc:expr, $field:ident) => {{
174+
result.set_item(stringify!($field), vm.new_pyobj((*$lc).$field), vm)?
109175
}};
110176
}
111177

112178
macro_rules! set_group_field {
113-
($field:ident) => {{
179+
($lc:expr, $field:ident) => {{
114180
result.set_item(
115181
stringify!($field),
116-
copy_grouping((*lc).$field, vm).into(),
182+
copy_grouping((*$lc).$field, vm).into(),
117183
vm,
118184
)?
119185
}};
120186
}
121187

122-
set_group_field!(mon_grouping);
123-
set_group_field!(grouping);
124-
set_int_field!(int_frac_digits);
125-
set_int_field!(frac_digits);
126-
set_int_field!(p_cs_precedes);
127-
set_int_field!(p_sep_by_space);
128-
set_int_field!(n_cs_precedes);
129-
set_int_field!(p_sign_posn);
130-
set_int_field!(n_sign_posn);
131-
set_string_field!(decimal_point);
132-
set_string_field!(thousands_sep);
133-
set_string_field!(int_curr_symbol);
134-
set_string_field!(currency_symbol);
135-
set_string_field!(mon_decimal_point);
136-
set_string_field!(mon_thousands_sep);
137-
set_int_field!(n_sep_by_space);
138-
set_string_field!(positive_sign);
139-
set_string_field!(negative_sign);
188+
let lc = super::localeconv();
189+
set_group_field!(lc, mon_grouping);
190+
set_group_field!(lc, grouping);
191+
set_int_field!(lc, int_frac_digits);
192+
set_int_field!(lc, frac_digits);
193+
set_int_field!(lc, p_cs_precedes);
194+
set_int_field!(lc, p_sep_by_space);
195+
set_int_field!(lc, n_cs_precedes);
196+
set_int_field!(lc, p_sign_posn);
197+
set_int_field!(lc, n_sign_posn);
198+
set_string_field!(lc, decimal_point);
199+
set_string_field!(lc, thousands_sep);
200+
set_string_field!(lc, int_curr_symbol);
201+
set_string_field!(lc, currency_symbol);
202+
set_string_field!(lc, mon_decimal_point);
203+
set_string_field!(lc, mon_thousands_sep);
204+
set_int_field!(lc, n_sep_by_space);
205+
set_string_field!(lc, positive_sign);
206+
set_string_field!(lc, negative_sign);
140207
}
141208
Ok(result)
142209
}
@@ -149,8 +216,17 @@ mod _locale {
149216
locale: OptionalArg<Option<PyStrRef>>,
150217
}
151218

219+
#[cfg(any(
220+
unix,
221+
windows,
222+
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
223+
))]
152224
#[pyfunction]
153225
fn setlocale(args: LocaleArgs, vm: &VirtualMachine) -> PyResult {
226+
let error = error(vm);
227+
if cfg!(windows) && (args.category < LC_ALL || args.category > LC_TIME) {
228+
return Err(vm.new_exception_msg(error, String::from("unsupported locale setting")));
229+
}
154230
unsafe {
155231
let result = match args.locale.flatten() {
156232
None => libc::setlocale(args.category, ptr::null()),
@@ -161,7 +237,6 @@ mod _locale {
161237
}
162238
};
163239
if result.is_null() {
164-
let error = error(vm);
165240
return Err(vm.new_exception_msg(error, String::from("unsupported locale setting")));
166241
}
167242
pystr_from_raw_cstr(vm, result)

0 commit comments

Comments
 (0)