Skip to content

Commit dfc1ab9

Browse files
authored
Merge pull request RustPython#7075 from youknowone/sys
Update test_sys from v3.14.3 and impl more sys module
2 parents 56f69b9 + b5ec923 commit dfc1ab9

6 files changed

Lines changed: 83 additions & 28 deletions

File tree

Lib/test/test_c_locale_coercion.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,6 @@ def setUpClass(cls):
243243
if not AVAILABLE_TARGETS:
244244
raise unittest.SkipTest("No C-with-UTF-8 locale available")
245245

246-
# TODO: RUSTPYTHON
247-
@unittest.expectedFailure
248246
def test_external_target_locale_configuration(self):
249247

250248
# Explicitly setting a target locale should give the same behaviour as

Lib/test/test_sys.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import builtins
22
import codecs
3-
# import _datetime # TODO: RUSTPYTHON
43
import gc
54
import io
65
import locale
@@ -210,7 +209,7 @@ class SysModuleTest(unittest.TestCase):
210209
def tearDown(self):
211210
test.support.reap_children()
212211

213-
@unittest.expectedFailure # TODO: RUSTPYTHON
212+
@unittest.expectedFailure # TODO: RUSTPYTHON; latin-1 codec not registered
214213
def test_exit(self):
215214
# call with two arguments
216215
self.assertRaises(TypeError, sys.exit, 42, 42)
@@ -352,7 +351,7 @@ def test_setrecursionlimit(self):
352351
finally:
353352
sys.setrecursionlimit(old_limit)
354353

355-
@unittest.skipIf(getattr(sys, '_rustpython_debugbuild', False), 'TODO: RUSTPYTHON; stack overflow on debug build')
354+
@unittest.skipIf(getattr(sys, "_rustpython_debugbuild", False), "TODO: RUSTPYTHON; stack overflow on debug build")
356355
def test_recursionlimit_recovery(self):
357356
if hasattr(sys, 'gettrace') and sys.gettrace():
358357
self.skipTest('fatal error if run with a trace function')
@@ -433,7 +432,6 @@ def test_getwindowsversion(self):
433432
# still has 5 elements
434433
maj, min, buildno, plat, csd = sys.getwindowsversion()
435434

436-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'call_tracing'
437435
def test_call_tracing(self):
438436
self.assertRaises(TypeError, sys.call_tracing, type, 2)
439437

@@ -499,8 +497,6 @@ def test_getframemodulename(self):
499497
self.assertIsNone(sys._getframemodulename(i))
500498

501499
# sys._current_frames() is a CPython-only gimmick.
502-
# XXX RUSTPYTHON: above comment is from original cpython test; not sure why the cpython_only decorator wasn't added
503-
@test.support.cpython_only
504500
@threading_helper.reap_threads
505501
@threading_helper.requires_working_threading()
506502
def test_current_frames(self):
@@ -568,7 +564,6 @@ def g456():
568564
leave_g.set()
569565
t.join()
570566

571-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute '_current_exceptions'
572567
@threading_helper.reap_threads
573568
@threading_helper.requires_working_threading()
574569
def test_current_exceptions(self):
@@ -856,7 +851,7 @@ def test_subinterp_intern_singleton(self):
856851
'''))
857852
self.assertTrue(sys._is_interned(s))
858853

859-
@unittest.expectedFailure # TODO: RUSTPYTHON; needs update for context_aware_warnings
854+
@unittest.expectedFailure # TODO: RUSTPYTHON; needs update for context_aware_warnings
860855
def test_sys_flags(self):
861856
self.assertTrue(sys.flags)
862857
attrs = ("debug",
@@ -897,7 +892,7 @@ def test_clear_type_cache(self):
897892
r"sys\._clear_type_cache\(\) is deprecated.*"):
898893
sys._clear_type_cache()
899894

900-
@unittest.skip('TODO: RUSTPYTHON; cp424 encoding not supported, causes panic')
895+
@unittest.skip("TODO: RUSTPYTHON; cp424 encoding not supported, causes panic")
901896
@force_not_colorized
902897
@support.requires_subprocess()
903898
def test_ioencoding(self):
@@ -1062,12 +1057,12 @@ def check_locale_surrogateescape(self, locale):
10621057
'stdout: surrogateescape\n'
10631058
'stderr: backslashreplace\n')
10641059

1065-
@unittest.expectedFailure # TODO: RUSTPYTHON
1060+
@unittest.expectedFailure # TODO: RUSTPYTHON; stderr: backslashreplace
10661061
@support.requires_subprocess()
10671062
def test_c_locale_surrogateescape(self):
10681063
self.check_locale_surrogateescape('C')
10691064

1070-
@unittest.expectedFailure # TODO: RUSTPYTHON
1065+
@unittest.expectedFailure # TODO: RUSTPYTHON; stderr: backslashreplace
10711066
@support.requires_subprocess()
10721067
def test_posix_locale_surrogateescape(self):
10731068
self.check_locale_surrogateescape('POSIX')
@@ -1192,7 +1187,6 @@ def __del__(self):
11921187
rc, stdout, stderr = assert_python_ok('-c', code)
11931188
self.assertEqual(stdout.rstrip(), b'True')
11941189

1195-
@unittest.expectedFailure # TODO: RUSTPYTHON; IndexError: list index out of range
11961190
def test_issue20602(self):
11971191
# sys.flags and sys.float_info were wiped during shutdown.
11981192
code = """if 1:
@@ -1225,7 +1219,7 @@ def __del__(self):
12251219
self.assertEqual(stdout.rstrip(), b"")
12261220
self.assertEqual(stderr.rstrip(), b"")
12271221

1228-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'getandroidapilevel'
1222+
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'getandroidapilevel'
12291223
@unittest.skipUnless(sys.platform == "android", "Android only")
12301224
def test_getandroidapilevel(self):
12311225
level = sys.getandroidapilevel()
@@ -1494,6 +1488,7 @@ def hook_func(args):
14941488
def test_custom_unraisablehook_fail(self):
14951489
_testcapi = import_helper.import_module('_testcapi')
14961490
from _testcapi import err_writeunraisable
1491+
14971492
def hook_func(*args):
14981493
raise Exception("hook_func failed")
14991494

@@ -1742,7 +1737,12 @@ def delx(self): del self.__x
17421737
x = property(getx, setx, delx, "")
17431738
check(x, size('5Pi'))
17441739
# PyCapsule
1745-
check(_datetime.datetime_CAPI, size('6P'))
1740+
try:
1741+
import _datetime
1742+
except ModuleNotFoundError:
1743+
pass
1744+
else:
1745+
check(_datetime.datetime_CAPI, size('6P'))
17461746
# rangeiterator
17471747
check(iter(range(1)), size('3l'))
17481748
check(iter(range(2**65)), size('3P'))
@@ -2227,7 +2227,7 @@ def test_jit_is_enabled(self):
22272227
assert_python_ok("-c", script.format(enabled=False), PYTHON_JIT="0")
22282228
assert_python_ok("-c", script.format(enabled=available), PYTHON_JIT="1")
22292229

2230-
@unittest.expectedFailure # TODO: RUSTPYTHON
2230+
@unittest.expectedFailure # TODO: RUSTPYTHON; ---
22312231
def test_jit_is_active(self):
22322232
available = sys._jit.is_available()
22332233
script = textwrap.dedent(

crates/vm/src/object/core.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,10 +1078,7 @@ impl PyObject {
10781078
Some(true) => Ok(()),
10791079
// we've been resurrected by __del__
10801080
Some(false) => Err(()),
1081-
None => {
1082-
warn!("couldn't run __del__ method for object");
1083-
Ok(())
1084-
}
1081+
None => Ok(()),
10851082
}
10861083
}
10871084

crates/vm/src/stdlib/sys.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ mod sys {
3333
use crate::{
3434
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
3535
builtins::{
36-
PyBaseExceptionRef, PyDictRef, PyFrozenSet, PyNamespace, PyStr, PyStrRef, PyTupleRef,
37-
PyTypeRef,
36+
PyBaseExceptionRef, PyDictRef, PyFrozenSet, PyNamespace, PyStr, PyStrRef, PyTuple,
37+
PyTupleRef, PyTypeRef,
3838
},
3939
common::{
4040
ascii,
@@ -789,8 +789,22 @@ mod sys {
789789

790790
#[pyfunction]
791791
fn exit(code: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
792-
let code = code.unwrap_or_none(vm);
793-
Err(vm.new_exception(vm.ctx.exceptions.system_exit.to_owned(), vec![code]))
792+
let status = code.unwrap_or_none(vm);
793+
let args = if let Some(status_tuple) = status.downcast_ref::<PyTuple>() {
794+
status_tuple.as_slice().to_vec()
795+
} else {
796+
vec![status]
797+
};
798+
let exc = vm.invoke_exception(vm.ctx.exceptions.system_exit.to_owned(), args)?;
799+
Err(exc)
800+
}
801+
802+
#[pyfunction]
803+
fn call_tracing(func: PyObjectRef, args: PyTupleRef, vm: &VirtualMachine) -> PyResult {
804+
// CPython temporarily enables tracing state around this call.
805+
// RustPython does not currently model the full C-level tracing toggles,
806+
// but call semantics (func(*args)) are matched.
807+
func.call(PosArgs::new(args.as_slice().to_vec()), vm)
794808
}
795809

796810
#[pyfunction]
@@ -1031,6 +1045,33 @@ mod sys {
10311045
Ok(dict)
10321046
}
10331047

1048+
/// Return a dictionary mapping each thread's identifier to its currently
1049+
/// active exception, or None if no exception is active.
1050+
#[cfg(feature = "threading")]
1051+
#[pyfunction]
1052+
fn _current_exceptions(vm: &VirtualMachine) -> PyResult<PyDictRef> {
1053+
use crate::AsObject;
1054+
use crate::vm::thread::get_all_current_exceptions;
1055+
1056+
let dict = vm.ctx.new_dict();
1057+
for (thread_id, exc) in get_all_current_exceptions(vm) {
1058+
let key = vm.ctx.new_int(thread_id);
1059+
let value = exc.map_or_else(|| vm.ctx.none(), |e| e.into());
1060+
dict.set_item(key.as_object(), value, vm)?;
1061+
}
1062+
1063+
Ok(dict)
1064+
}
1065+
1066+
#[cfg(not(feature = "threading"))]
1067+
#[pyfunction]
1068+
fn _current_exceptions(vm: &VirtualMachine) -> PyResult<PyDictRef> {
1069+
let dict = vm.ctx.new_dict();
1070+
let key = vm.ctx.new_int(0);
1071+
dict.set_item(key.as_object(), vm.topmost_exception().to_pyobject(vm), vm)?;
1072+
Ok(dict)
1073+
}
1074+
10341075
/// Stub for non-threading builds - returns empty dict
10351076
#[cfg(not(feature = "threading"))]
10361077
#[pyfunction]

crates/vm/src/types/structseq.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::common::lock::LazyLock;
22
use crate::{
33
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func,
4-
builtins::{PyBaseExceptionRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
4+
builtins::{PyBaseExceptionRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
55
class::{PyClassImpl, StaticType},
66
function::{Either, FuncArgs, PyComparisonValue, PyMethodDef, PyMethodFlags},
77
iter::PyExactSizeIterator,
@@ -225,7 +225,21 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static {
225225
} else {
226226
(String::new(), "...")
227227
};
228-
let repr_str = format!("{}({}{})", Self::TP_NAME, body, suffix);
228+
// Build qualified name: if MODULE_NAME is already in TP_NAME, use it directly.
229+
// Otherwise, check __module__ attribute (set by #[pymodule] at runtime).
230+
let type_name = if Self::MODULE_NAME.is_some() {
231+
alloc::borrow::Cow::Borrowed(Self::TP_NAME)
232+
} else {
233+
let typ = zelf.class();
234+
match typ.get_attr(identifier!(vm.ctx, __module__)) {
235+
Some(module) if module.downcastable::<PyStr>() => {
236+
let module_str = module.downcast_ref::<PyStr>().unwrap();
237+
alloc::borrow::Cow::Owned(format!("{}.{}", module_str.as_str(), Self::NAME))
238+
}
239+
_ => alloc::borrow::Cow::Borrowed(Self::TP_NAME),
240+
}
241+
};
242+
let repr_str = format!("{}({}{})", type_name, body, suffix);
229243
Ok(vm.ctx.new_str(repr_str))
230244
}
231245

crates/vm/src/vm/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,12 @@ impl VirtualMachine {
397397
let errors = if fd == 2 {
398398
Some("backslashreplace")
399399
} else {
400-
self.state.config.settings.stdio_errors.as_deref()
400+
self.state
401+
.config
402+
.settings
403+
.stdio_errors
404+
.as_deref()
405+
.or(Some("surrogateescape"))
401406
};
402407

403408
let stdio = self.call_method(

0 commit comments

Comments
 (0)