heaptype __qualname__ (#5848)

This commit is contained in:
Jeong, YunWon
2025-06-27 19:58:48 +09:00
committed by GitHub
parent f0c7cb26f7
commit 5504f6d9e8
5 changed files with 78 additions and 39 deletions

View File

@@ -4987,8 +4987,6 @@ order (MRO) for bases """
self.assertIn("__dict__", Base.__dict__)
self.assertNotIn("__dict__", Sub.__dict__)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bound_method_repr(self):
class Foo:
def method(self):
@@ -5126,6 +5124,8 @@ class DictProxyTests(unittest.TestCase):
self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
'__weakref__', 'meth'])
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'trace function introduces __local__')
def test_iter_values(self):

View File

@@ -2960,8 +2960,6 @@ class ProtocolTests(BaseTestCase):
self.assertNotIsInstance(Capybara('a'), HasX)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_everything_implements_empty_protocol(self):
@runtime_checkable
class Empty(Protocol):
@@ -9238,8 +9236,6 @@ SpecialAttrsT = typing.TypeVar('SpecialAttrsT', int, float, complex)
class SpecialAttrsTests(BaseTestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_special_attrs(self):
cls_to_check = {
# ABC classes

View File

@@ -616,6 +616,20 @@ impl Py<PyDict> {
}
}
pub fn pop_item<K: DictKey + ?Sized>(
&self,
key: &K,
vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
if self.exact_dict(vm) {
self.entries.remove_if_exists(vm, key)
} else {
let value = self.as_object().get_item(key, vm)?;
self.as_object().del_item(key, vm)?;
Ok(Some(value))
}
}
pub fn get_chain<K: DictKey + ?Sized>(
&self,
other: &Self,

View File

@@ -58,8 +58,10 @@ unsafe impl crate::object::Traverse for PyType {
}
}
// PyHeapTypeObject in CPython
pub struct HeapTypeExt {
pub name: PyRwLock<PyStrRef>,
pub qualname: PyRwLock<PyStrRef>,
pub slots: Option<PyTupleTyped<PyStrRef>>,
pub sequence_methods: PySequenceMethods,
pub mapping_methods: PyMappingMethods,
@@ -143,6 +145,16 @@ impl PyPayload for PyType {
}
}
fn downcast_qualname(value: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
match value.downcast::<PyStr>() {
Ok(value) => Ok(value),
Err(value) => Err(vm.new_type_error(format!(
"can only assign string to __qualname__, not '{}'",
value.class().name()
))),
}
}
impl PyType {
pub fn new_simple_heap(
name: &str,
@@ -171,7 +183,8 @@ impl PyType {
let name = ctx.new_str(name);
let heaptype_ext = HeapTypeExt {
name: PyRwLock::new(name),
name: PyRwLock::new(name.clone()),
qualname: PyRwLock::new(name),
slots: None,
sequence_methods: PySequenceMethods::default(),
mapping_methods: PyMappingMethods::default(),
@@ -577,19 +590,12 @@ impl PyType {
#[pygetset]
pub fn __qualname__(&self, vm: &VirtualMachine) -> PyObjectRef {
self.attributes
.read()
.get(identifier!(vm, __qualname__))
.cloned()
// We need to exclude this method from going into recursion:
.and_then(|found| {
if found.fast_isinstance(vm.ctx.types.getset_type) {
None
} else {
Some(found)
}
})
.unwrap_or_else(|| vm.ctx.new_str(self.name().deref()).into())
if let Some(ref heap_type) = self.heaptype_ext {
heap_type.qualname.read().clone().into()
} else {
// For static types, return the name
vm.ctx.new_str(self.name().deref()).into()
}
}
#[pygetset(setter)]
@@ -607,16 +613,14 @@ impl PyType {
self.name()
))
})?;
if !value.class().fast_issubclass(vm.ctx.types.str_type) {
return Err(vm.new_type_error(format!(
"can only assign string to {}.__qualname__, not '{}'",
self.name(),
value.class().name()
)));
}
self.attributes
.write()
.insert(identifier!(vm, __qualname__), value);
let str_value = downcast_qualname(value, vm)?;
let heap_type = self
.heaptype_ext
.as_ref()
.expect("HEAPTYPE should have heaptype_ext");
*heap_type.qualname.write() = str_value;
Ok(())
}
@@ -856,6 +860,14 @@ impl Constructor for PyType {
(metatype, base.to_owned(), bases)
};
let qualname = dict
.pop_item(identifier!(vm, __qualname__).as_object(), vm)?
.map(|obj| downcast_qualname(obj, vm))
.transpose()?
.unwrap_or_else(|| {
// If __qualname__ is not provided, we can use the name as default
name.clone()
});
let mut attributes = dict.to_attributes(vm);
if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__)) {
@@ -882,10 +894,6 @@ impl Constructor for PyType {
}
}
attributes
.entry(identifier!(vm, __qualname__))
.or_insert_with(|| name.clone().into());
if attributes.get(identifier!(vm, __eq__)).is_some()
&& attributes.get(identifier!(vm, __hash__)).is_none()
{
@@ -952,6 +960,7 @@ impl Constructor for PyType {
};
let heaptype_ext = HeapTypeExt {
name: PyRwLock::new(name),
qualname: PyRwLock::new(qualname),
slots: heaptype_slots.to_owned(),
sequence_methods: PySequenceMethods::default(),
mapping_methods: PyMappingMethods::default(),

View File

@@ -366,7 +366,7 @@ impl<T: Clone> Dict<T> {
where
K: DictKey + ?Sized,
{
if self.delete_if_exists(vm, key)? {
if self.remove_if_exists(vm, key)?.is_some() {
Ok(())
} else {
Err(vm.new_key_error(key.to_pyobject(vm)))
@@ -377,25 +377,45 @@ impl<T: Clone> Dict<T> {
where
K: DictKey + ?Sized,
{
self.delete_if(vm, key, |_| Ok(true))
self.remove_if_exists(vm, key).map(|opt| opt.is_some())
}
pub fn delete_if<K, F>(&self, vm: &VirtualMachine, key: &K, pred: F) -> PyResult<bool>
where
K: DictKey + ?Sized,
F: Fn(&T) -> PyResult<bool>,
{
self.remove_if(vm, key, pred).map(|opt| opt.is_some())
}
pub fn remove_if_exists<K>(&self, vm: &VirtualMachine, key: &K) -> PyResult<Option<T>>
where
K: DictKey + ?Sized,
{
self.remove_if(vm, key, |_| Ok(true))
}
/// pred should be VERY CAREFUL about what it does as it is called while
/// the dict's internal mutex is held
pub(crate) fn delete_if<K, F>(&self, vm: &VirtualMachine, key: &K, pred: F) -> PyResult<bool>
pub(crate) fn remove_if<K, F>(
&self,
vm: &VirtualMachine,
key: &K,
pred: F,
) -> PyResult<Option<T>>
where
K: DictKey + ?Sized,
F: Fn(&T) -> PyResult<bool>,
{
let hash = key.key_hash(vm)?;
let deleted = loop {
let removed = loop {
let lookup = self.lookup(vm, key, hash, None)?;
match self.pop_inner_if(lookup, &pred)? {
ControlFlow::Break(entry) => break entry,
ControlFlow::Continue(()) => continue,
}
};
Ok(deleted.is_some())
Ok(removed.map(|entry| entry.value))
}
pub fn delete_or_insert(&self, vm: &VirtualMachine, key: &PyObject, value: T) -> PyResult<()> {