Skip to content

Commit 591d12a

Browse files
committed
fix test_builtin
1 parent 7f4d308 commit 591d12a

File tree

4 files changed

+50
-24
lines changed

4 files changed

+50
-24
lines changed

Lib/test/test_builtin.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def check_iter_pickle(self, it, seq, proto):
153153
it = pickle.loads(d)
154154
self.assertEqual(list(it), seq[1:])
155155

156-
# TODO: RUSTPYTHON
156+
# TODO: RUSTPYTHON - ImportWarning not being captured properly
157157
@unittest.expectedFailure
158158
def test_import(self):
159159
__import__('sys')
@@ -2012,21 +2012,18 @@ def test_construct_singletons(self):
20122012
self.assertRaises(TypeError, tp, 1, 2)
20132013
self.assertRaises(TypeError, tp, a=1, b=2)
20142014

2015-
# TODO: RUSTPYTHON
2016-
@unittest.expectedFailure
2017-
def test_warning_notimplemented(self):
2018-
# Issue #35712: NotImplemented is a sentinel value that should never
2015+
def test_bool_notimplemented(self):
2016+
# GH-79893: NotImplemented is a sentinel value that should never
20192017
# be evaluated in a boolean context (virtually all such use cases
20202018
# are a result of accidental misuse implementing rich comparison
20212019
# operations in terms of one another).
2022-
# For the time being, it will continue to evaluate as a true value, but
2023-
# issue a deprecation warning (with the eventual intent to make it
2024-
# a TypeError).
2025-
self.assertWarns(DeprecationWarning, bool, NotImplemented)
2026-
with self.assertWarns(DeprecationWarning):
2027-
self.assertTrue(NotImplemented)
2028-
with self.assertWarns(DeprecationWarning):
2029-
self.assertFalse(not NotImplemented)
2020+
msg = "NotImplemented should not be used in a boolean context"
2021+
self.assertRaisesRegex(TypeError, msg, bool, NotImplemented)
2022+
with self.assertRaisesRegex(TypeError, msg):
2023+
if NotImplemented:
2024+
pass
2025+
with self.assertRaisesRegex(TypeError, msg):
2026+
not NotImplemented
20302027

20312028

20322029
class TestBreakpoint(unittest.TestCase):
@@ -2401,8 +2398,6 @@ def test_type_nokwargs(self):
24012398
with self.assertRaises(TypeError):
24022399
type('a', (), dict={})
24032400

2404-
# TODO: RUSTPYTHON
2405-
@unittest.expectedFailure
24062401
def test_type_name(self):
24072402
for name in 'A', '\xc4', '\U0001f40d', 'B.A', '42', '':
24082403
with self.subTest(name=name):
@@ -2452,8 +2447,6 @@ def test_type_qualname(self):
24522447
A.__qualname__ = b'B'
24532448
self.assertEqual(A.__qualname__, 'D.E')
24542449

2455-
# TODO: RUSTPYTHON
2456-
@unittest.expectedFailure
24572450
def test_type_doc(self):
24582451
for doc in 'x', '\xc4', '\U0001f40d', 'x\x00y', b'x', 42, None:
24592452
A = type('A', (), {'__doc__': doc})

crates/vm/src/builtins/singletons.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,11 @@ impl Constructor for PyNotImplemented {
103103
}
104104
}
105105

106-
#[pyclass(with(Constructor))]
106+
#[pyclass(with(Constructor, AsNumber))]
107107
impl PyNotImplemented {
108-
// TODO: As per https://bugs.python.org/issue35712, using NotImplemented
109-
// in boolean contexts will need to raise a DeprecationWarning in 3.9
110-
// and, eventually, a TypeError.
111108
#[pymethod]
112-
const fn __bool__(&self) -> bool {
113-
true
109+
fn __bool__(&self, vm: &VirtualMachine) -> PyResult<bool> {
110+
Err(vm.new_type_error("NotImplemented should not be used in a boolean context".to_owned()))
114111
}
115112

116113
#[pymethod]
@@ -119,6 +116,20 @@ impl PyNotImplemented {
119116
}
120117
}
121118

119+
impl AsNumber for PyNotImplemented {
120+
fn as_number() -> &'static PyNumberMethods {
121+
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
122+
boolean: Some(|_number, vm| {
123+
Err(vm.new_type_error(
124+
"NotImplemented should not be used in a boolean context".to_owned(),
125+
))
126+
}),
127+
..PyNumberMethods::NOT_IMPLEMENTED
128+
};
129+
&AS_NUMBER
130+
}
131+
}
132+
122133
impl Representable for PyNotImplemented {
123134
#[inline]
124135
fn repr(_zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {

crates/vm/src/builtins/str.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ impl PyStr {
441441
self.data.as_str()
442442
}
443443

444-
fn ensure_valid_utf8(&self, vm: &VirtualMachine) -> PyResult<()> {
444+
pub(crate) fn ensure_valid_utf8(&self, vm: &VirtualMachine) -> PyResult<()> {
445445
if self.is_utf8() {
446446
Ok(())
447447
} else {

crates/vm/src/builtins/type.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,7 @@ impl PyType {
11631163
if name.as_bytes().contains(&0) {
11641164
return Err(vm.new_value_error("type name must not contain null characters"));
11651165
}
1166+
name.ensure_valid_utf8(vm)?;
11661167

11671168
// Use std::mem::replace to swap the new value in and get the old value out,
11681169
// then drop the old value after releasing the lock (similar to CPython's Py_SETREF)
@@ -1254,6 +1255,7 @@ impl Constructor for PyType {
12541255
if name.as_bytes().contains(&0) {
12551256
return Err(vm.new_value_error("type name must not contain null characters"));
12561257
}
1258+
name.ensure_valid_utf8(vm)?;
12571259

12581260
let (metatype, base, bases, base_is_type) = if bases.is_empty() {
12591261
let base = vm.ctx.types.object_type.to_owned();
@@ -1306,6 +1308,13 @@ impl Constructor for PyType {
13061308
});
13071309
let mut attributes = dict.to_attributes(vm);
13081310

1311+
// Check __doc__ for surrogates - CPython raises UnicodeEncodeError during type creation
1312+
if let Some(doc) = attributes.get(identifier!(vm, __doc__)) {
1313+
if let Some(doc_str) = doc.downcast_ref::<PyStr>() {
1314+
doc_str.ensure_valid_utf8(vm)?;
1315+
}
1316+
}
1317+
13091318
if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__))
13101319
&& f.class().is(vm.ctx.types.function_type)
13111320
{
@@ -1340,6 +1349,13 @@ impl Constructor for PyType {
13401349

13411350
let (heaptype_slots, add_dict): (Option<PyRef<PyTuple<PyStrRef>>>, bool) =
13421351
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1352+
// Check if __slots__ is bytes - not allowed
1353+
if x.class().is(vm.ctx.types.bytes_type) {
1354+
return Err(vm.new_type_error(
1355+
"__slots__ items must be strings, not 'bytes'".to_owned(),
1356+
));
1357+
}
1358+
13431359
let slots = if x.class().is(vm.ctx.types.str_type) {
13441360
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
13451361
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
@@ -1348,6 +1364,12 @@ impl Constructor for PyType {
13481364
let elements = {
13491365
let mut elements = Vec::new();
13501366
while let PyIterReturn::Return(element) = iter.next(vm)? {
1367+
// Check if any slot item is bytes
1368+
if element.class().is(vm.ctx.types.bytes_type) {
1369+
return Err(vm.new_type_error(
1370+
"__slots__ items must be strings, not 'bytes'".to_owned(),
1371+
));
1372+
}
13511373
elements.push(element);
13521374
}
13531375
elements

0 commit comments

Comments
 (0)