Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4987,8 +4987,6 @@ class Sub(Base):
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):
Expand Down Expand Up @@ -5126,6 +5124,8 @@ def test_iter_keys(self):
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):
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2960,8 +2960,6 @@ def __init__(self, y):
self.assertNotIsInstance(Capybara('a'), HasX)


# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_everything_implements_empty_protocol(self):
@runtime_checkable
class Empty(Protocol):
Expand Down Expand Up @@ -9238,8 +9236,6 @@ def test_no_isinstance(self):


class SpecialAttrsTests(BaseTestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_special_attrs(self):
cls_to_check = {
# ABC classes
Expand Down
14 changes: 14 additions & 0 deletions vm/src/builtins/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
65 changes: 37 additions & 28 deletions vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)]
Expand All @@ -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(())
}

Expand Down Expand Up @@ -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__)) {
Expand All @@ -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()
{
Expand Down Expand Up @@ -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(),
Expand Down
30 changes: 25 additions & 5 deletions vm/src/dict_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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<()> {
Expand Down
Loading