mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Add payload views and Py handle shortcuts (#8812)
* Add payload views and Py handle shortcuts PyTuple::payload_slice, PyBytes::payload_bytes, and PyStr::payload_wtf8 expose the payload data. Common payload methods are forwarded from Py<T>. Assisted-by: Grok CLI:grok-4.7 * Call PyTuple::as_slice from payload_slice `&*self` tripped clippy borrow_deref_ref and needless_borrow. Assisted-by: Grok CLI:grok-4.7 * Use Element.is_empty in the truth-value check len() != 0 trips clippy::len_zero once is_empty is available. Assisted-by: Grok CLI:grok-4.7 * Drop redundant payload view aliases as_bytes, as_wtf8, and as_slice already forward to the payload methods. Assisted-by: Grok CLI:grok-4.7 * Forward more payload methods from Py handles Frame iframe, bytearray buffer, set add, exception args and traceback, and length or lookup helpers are available on Py<T>. Assisted-by: Grok CLI:grok-4.7 * Read type slots through payload in macros Generated subclass checks and MRO slot inheritance no longer use Deref to reach PyType.slots or attributes. Assisted-by: Grok CLI:grok-4.7 * Index a few tuples through as_slice These Py<PyTuple> indexes no longer rely on Deref to [T]. Assisted-by: Grok CLI:grok-4.7 * Read type slots through Py<PyType>::slots obj.class().slots and builtin type slots now call slots() instead of the payload field. Assisted-by: Grok CLI:grok-4.7 * Read type version and heap layout through Py<PyType> Inline-cache checks use tp_version_tag(). Heap-type weakref checks use heaptype_ext(). Assisted-by: Grok CLI:grok-4.7 * Call slots() on Py type handles Type creation, type methods, and frame specialization read slots through Py<PyType> instead of the payload field. Assisted-by: Grok CLI:grok-4.7 * Use as_bytes, as_slice, and traceback accessors Bytes lengths go through as_bytes(). Tuple len, emptiness, and iteration go through as_slice(). Exception handles use traceback(). Assisted-by: Grok CLI:grok-4.7 * Drop shortcuts that only forward payload methods Tuple length and iteration go through as_slice. Dict and list expose len. Set add stays on the payload. Assisted-by: Grok CLI:grok-4.7 * Drop dict and list len shortcuts len without is_empty fails clippy. Tuple element lookup uses as_slice().get instead of iter().nth. Assisted-by: Grok CLI:grok-4.7
This commit is contained in:
1 parent
5d15791e11
commit
0ebc6570e3
43 files changed
+286
-201
No files matched your search
@@ -257,7 +257,7 @@ pub unsafe extern "C" fn PyException_GetTraceback(exc: *mut PyObject) -> *mut Py
|
||||
with_vm(|vm| {
|
||||
let exc = unsafe { exc.assume_borrowed_and_cast::<PyBaseException>(vm) }?;
|
||||
let tb = exc
|
||||
.__traceback__()
|
||||
.traceback()
|
||||
.map(|tb| tb.into_object().into_raw().as_ptr())
|
||||
.unwrap_or_default();
|
||||
Ok(tb)
|
||||
|
||||
@@ -764,7 +764,7 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
|
||||
|
||||
#[inline]
|
||||
unsafe fn validate_downcastable_from(obj: &::rustpython_vm::PyObject) -> bool {
|
||||
<Self as ::rustpython_vm::class::PyClassDef>::BASICSIZE <= obj.class().slots.basicsize && obj.class().fast_issubclass(<Self as ::rustpython_vm::class::StaticType>::static_type())
|
||||
<Self as ::rustpython_vm::class::PyClassDef>::BASICSIZE <= obj.class().payload().slots.basicsize && obj.class().fast_issubclass(<Self as ::rustpython_vm::class::StaticType>::static_type())
|
||||
}
|
||||
|
||||
fn class(ctx: &::rustpython_vm::vm::Context) -> &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType> {
|
||||
|
||||
@@ -755,7 +755,7 @@ impl ModuleItem for ClassItem {
|
||||
// module resolution, e.g. TypeAliasType)
|
||||
{
|
||||
let module_key = rustpython_vm::identifier!(ctx, __module__);
|
||||
let has_module_getset = new_class.attributes
|
||||
let has_module_getset = new_class.payload().attributes
|
||||
.get(module_key)
|
||||
.is_some_and(|v| v.downcastable::<rustpython_vm::builtins::PyGetSet>());
|
||||
if !has_module_getset {
|
||||
@@ -849,7 +849,7 @@ impl ModuleItem for StructSequenceItem {
|
||||
let new_class = <#pytype_ident as ::rustpython_vm::class::PyClassImpl>::make_static_type();
|
||||
{
|
||||
let module_key = rustpython_vm::identifier!(ctx, __module__);
|
||||
let has_module_getset = new_class.attributes
|
||||
let has_module_getset = new_class.payload().attributes
|
||||
.get(module_key)
|
||||
.is_some_and(|v| v.downcastable::<rustpython_vm::builtins::PyGetSet>());
|
||||
if !has_module_getset {
|
||||
|
||||
@@ -322,7 +322,7 @@ pub(crate) mod _asyncio {
|
||||
|
||||
// Save the original traceback for later restoration
|
||||
if let Ok(exc_ref) = exc.clone().downcast::<PyBaseException>() {
|
||||
let tb = exc_ref.__traceback__().map(|tb| tb.into());
|
||||
let tb = exc_ref.traceback().map(|tb| tb.into());
|
||||
*zelf.fut_exception_tb.write() = tb;
|
||||
}
|
||||
|
||||
@@ -2296,7 +2296,7 @@ pub(crate) mod _asyncio {
|
||||
} else {
|
||||
task.base.fut_state.store(FutureState::Finished);
|
||||
// Save the original traceback for later restoration
|
||||
let tb = exc.__traceback__().map(|tb| tb.into());
|
||||
let tb = exc.traceback().map(|tb| tb.into());
|
||||
*task.base.fut_exception_tb.write() = tb;
|
||||
*task.base.fut_exception.write() = Some(exc.clone().into());
|
||||
task.base.fut_log_tb.store(true, Ordering::Relaxed);
|
||||
|
||||
@@ -239,6 +239,10 @@ pub(crate) mod _elementtree {
|
||||
self.inner.read().children.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
fn child(&self, index: usize) -> Option<PyObjectRef> {
|
||||
self.inner.read().children.get(index).cloned()
|
||||
}
|
||||
@@ -1209,7 +1213,7 @@ pub(crate) mod _elementtree {
|
||||
None,
|
||||
vm,
|
||||
)?;
|
||||
Ok(zelf.len() != 0)
|
||||
Ok(!zelf.is_empty())
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ impl PyObject {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn try_to_bool_slow(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
let slots = &self.class().slots;
|
||||
let slots = self.class().slots();
|
||||
|
||||
// 1. Try nb_bool slot first
|
||||
if let Some(nb_bool) = slots.as_number.boolean.load() {
|
||||
|
||||
@@ -550,6 +550,11 @@ impl PyBytes {
|
||||
|
||||
#[pyclass]
|
||||
impl Py<PyBytes> {
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.payload().as_bytes()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __reduce_ex__(
|
||||
&self,
|
||||
@@ -637,7 +642,7 @@ impl AsBuffer for PyBytes {
|
||||
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
|
||||
let buf = PyBuffer::new(
|
||||
zelf.to_owned().into(),
|
||||
BufferDescriptor::simple(zelf.len(), true),
|
||||
BufferDescriptor::simple(zelf.as_bytes().len(), true),
|
||||
&BUFFER_METHODS,
|
||||
);
|
||||
Ok(buf)
|
||||
@@ -647,7 +652,9 @@ impl AsBuffer for PyBytes {
|
||||
impl AsMapping for PyBytes {
|
||||
fn as_mapping() -> &'static PyMappingMethods {
|
||||
static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
|
||||
length: atomic_func!(|mapping, _vm| Ok(PyBytes::mapping_downcast(mapping).len())),
|
||||
length: atomic_func!(|mapping, _vm| {
|
||||
Ok(PyBytes::mapping_downcast(mapping).as_bytes().len())
|
||||
}),
|
||||
subscript: atomic_func!(
|
||||
|mapping, needle, vm| PyBytes::mapping_downcast(mapping)._getitem(needle, vm)
|
||||
),
|
||||
@@ -660,7 +667,7 @@ impl AsMapping for PyBytes {
|
||||
impl AsSequence for PyBytes {
|
||||
fn as_sequence() -> &'static PySequenceMethods {
|
||||
static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
|
||||
length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).len())),
|
||||
length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).as_bytes().len())),
|
||||
concat: atomic_func!(|seq, other, vm| {
|
||||
PyBytes::sequence_downcast(seq)
|
||||
.inner
|
||||
@@ -769,7 +776,7 @@ impl PyPayload for PyBytesIterator {
|
||||
impl PyBytesIterator {
|
||||
#[pymethod]
|
||||
fn __length_hint__(&self) -> usize {
|
||||
self.internal.lock().length_hint(|obj| obj.len())
|
||||
self.internal.lock().length_hint(|obj| obj.as_bytes().len())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -787,7 +794,7 @@ impl PyBytesIterator {
|
||||
fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
self.internal
|
||||
.lock()
|
||||
.set_state(&state, |obj, pos| pos.min(obj.len()), vm)
|
||||
.set_state(&state, |obj, pos| pos.min(obj.as_bytes().len()), vm)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -560,5 +560,5 @@ fn vectorcall_float(
|
||||
#[rustfmt::skip] // to avoid line splitting
|
||||
pub(crate) fn init(context: &'static Context) {
|
||||
PyFloat::extend_class(context, context.types.float_type);
|
||||
context.types.float_type.slots.vectorcall.store(Some(vectorcall_float));
|
||||
context.types.float_type.slots().vectorcall.store(Some(vectorcall_float));
|
||||
}
|
||||
@@ -369,11 +369,11 @@ fn subs_tvars(
|
||||
|
||||
for arg in sub_params.iter() {
|
||||
if let Some(idx) = tuple_index(params.as_slice(), arg) {
|
||||
let param = ¶ms[idx];
|
||||
let param = ¶ms.as_slice()[idx];
|
||||
let substituted_arg = &arg_items[idx];
|
||||
|
||||
// Check if this is a TypeVarTuple (has tp_iter)
|
||||
if param.class().slots.iter.load().is_some()
|
||||
if param.class().slots().iter.load().is_some()
|
||||
&& substituted_arg.try_to_ref::<PyTuple>(vm).is_ok()
|
||||
{
|
||||
// TypeVarTuple case - extend with tuple elements
|
||||
|
||||
@@ -128,6 +128,18 @@ impl ToPyObject for Vec<PyObjectRef> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Py<PyList> {
|
||||
#[inline]
|
||||
pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> {
|
||||
self.payload().borrow_vec()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
|
||||
self.payload().borrow_vec_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl PyList {
|
||||
#[deprecated(note = "use PyList::from(...).into_ref() instead")]
|
||||
pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
|
||||
@@ -792,7 +804,7 @@ fn object_lt(
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<bool> {
|
||||
#[allow(unpredictable_function_pointer_comparisons)]
|
||||
if a.class().slots.richcompare.load() != Some(cmp) {
|
||||
if a.class().slots().richcompare.load() != Some(cmp) {
|
||||
return a.rich_compare_bool(b, PyComparisonOp::Lt, vm);
|
||||
}
|
||||
match cmp(a, b, PyComparisonOp::Lt, vm)? {
|
||||
|
||||
@@ -1474,7 +1474,12 @@ static BUFFER_WRAPPER_METHODS: BufferMethods = BufferMethods {
|
||||
// A native release runs when the memoryview itself is torn down; only a
|
||||
// Python-level hook on a foreign exporter has to be called here.
|
||||
if !mv.buffer.obj.is(&wrapper.exporter)
|
||||
&& wrapper.exporter.class().slots.python_release_buffer.load()
|
||||
&& wrapper
|
||||
.exporter
|
||||
.class()
|
||||
.slots()
|
||||
.python_release_buffer
|
||||
.load()
|
||||
{
|
||||
call_python_release_buffer(&wrapper.exporter, mv.clone());
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ fn type_slot_names(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<Option<sup
|
||||
// object_getstate_default
|
||||
fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine) -> PyResult {
|
||||
// Check itemsize
|
||||
if required && obj.class().slots.itemsize > 0 {
|
||||
if required && obj.class().slots().itemsize > 0 {
|
||||
return Err(vm.new_type_error(format!("cannot pickle {:.200} objects", obj.class().name())));
|
||||
}
|
||||
|
||||
@@ -205,22 +205,22 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
|
||||
|
||||
if required {
|
||||
// Start with PyBaseObject_Type's basicsize
|
||||
let mut basicsize = vm.ctx.types.object_type.slots.basicsize;
|
||||
let mut basicsize = vm.ctx.types.object_type.slots().basicsize;
|
||||
|
||||
// Add __dict__ size if type has dict
|
||||
if obj.class().slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
|
||||
if obj.class().slots().flags.has_feature(PyTypeFlags::HAS_DICT) {
|
||||
basicsize += core::mem::size_of::<PyObjectRef>();
|
||||
}
|
||||
|
||||
// Add __weakref__ size if type has weakref support
|
||||
let has_weakref = if let Some(ref ext) = obj.class().heaptype_ext {
|
||||
let has_weakref = if let Some(ext) = obj.class().heaptype_ext() {
|
||||
match &ext.slots {
|
||||
None => true, // Heap type without __slots__ has automatic weakref
|
||||
Some(slots) => slots.iter().any(|s| s.as_bytes() == b"__weakref__"),
|
||||
}
|
||||
} else {
|
||||
let weakref_name = vm.ctx.intern_str("__weakref__");
|
||||
obj.class().attributes.contains(weakref_name)
|
||||
obj.class().attributes().contains(weakref_name)
|
||||
};
|
||||
if has_weakref {
|
||||
basicsize += core::mem::size_of::<PyObjectRef>();
|
||||
@@ -232,7 +232,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
|
||||
}
|
||||
|
||||
// Fail if actual type's basicsize > expected basicsize
|
||||
if obj.class().slots.basicsize > basicsize {
|
||||
if obj.class().slots().basicsize > basicsize {
|
||||
return Err(vm.new_type_error(format!("cannot pickle '{}' object", obj.class().name())));
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ impl PyBaseObject {
|
||||
}
|
||||
}
|
||||
PyComparisonOp::Ne => {
|
||||
let cmp = zelf.class().slots.richcompare.load().unwrap();
|
||||
let cmp = zelf.class().slots().richcompare.load().unwrap();
|
||||
let value = match cmp(zelf, other, PyComparisonOp::Eq, vm)? {
|
||||
Either::A(obj) => PyArithmeticValue::from_object(vm, obj)
|
||||
.map(|obj| obj.try_to_bool(vm))
|
||||
@@ -524,7 +524,7 @@ impl PyBaseObject {
|
||||
|
||||
#[pymethod]
|
||||
fn __sizeof__(zelf: PyObjectRef) -> usize {
|
||||
zelf.class().slots.basicsize
|
||||
zelf.class().slots().basicsize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +580,11 @@ pub fn object_generic_set_dict(
|
||||
pub(crate) fn init(ctx: &'static Context) {
|
||||
// Manually set alloc/init slots - derive macro doesn't generate extend_slots
|
||||
// for trait impl that overrides #[pyslot] method
|
||||
ctx.types.object_type.slots.alloc.store(Some(generic_alloc));
|
||||
ctx.types
|
||||
.object_type
|
||||
.slots()
|
||||
.alloc
|
||||
.store(Some(generic_alloc));
|
||||
ctx.types
|
||||
.object_type
|
||||
.slots
|
||||
|
||||
@@ -629,6 +629,16 @@ impl PyStr {
|
||||
}
|
||||
|
||||
impl Py<PyStr> {
|
||||
#[inline]
|
||||
pub fn as_wtf8(&self) -> &Wtf8 {
|
||||
self.payload().as_wtf8()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.payload().as_bytes()
|
||||
}
|
||||
|
||||
pub fn as_utf8(&self) -> Option<&Py<PyUtf8Str>> {
|
||||
if self.is_utf8() {
|
||||
// SAFETY: is_utf8() guarantees the PyUtf8Str invariant.
|
||||
|
||||
@@ -295,7 +295,7 @@ impl<'a, R> core::iter::IntoIterator for &'a PyTuple<R> {
|
||||
type IntoIter = core::slice::Iter<'a, R>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
self.as_slice().iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ impl<'a, R> core::iter::IntoIterator for &'a Py<PyTuple<R>> {
|
||||
type IntoIter = core::slice::Iter<'a, R>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
PyTuple::as_slice(self).iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,23 +313,6 @@ impl<R> PyTuple<R> {
|
||||
pub fn as_slice(&self) -> &[R] {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elements.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, R> {
|
||||
self.elements.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl PyTuple<PyObjectRef> {
|
||||
@@ -367,7 +350,7 @@ impl PyTuple<PyObjectRef> {
|
||||
}
|
||||
|
||||
fn repeat(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
Ok(if zelf.elements.is_empty() || value == 0 {
|
||||
Ok(if zelf.as_slice().is_empty() || value == 0 {
|
||||
vm.ctx.empty_tuple.clone()
|
||||
} else if value == 1 && zelf.class().is(vm.ctx.types.tuple_type) {
|
||||
// Special case: when some `tuple` is multiplied by `1`,
|
||||
@@ -387,6 +370,11 @@ impl PyTuple<PyObjectRef> {
|
||||
}
|
||||
|
||||
impl Py<PyTuple> {
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[PyObjectRef] {
|
||||
self.payload().as_slice()
|
||||
}
|
||||
|
||||
pub fn extract_tuple<'a, T: FromPyTuple<'a>>(&'a self, vm: &VirtualMachine) -> PyResult<T> {
|
||||
T::from_pytuple(self, vm)
|
||||
}
|
||||
@@ -433,12 +421,13 @@ impl PyTuple {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyArithmeticValue<PyRef<Self>> {
|
||||
let added = other.downcast::<Self>().map(|other| {
|
||||
if other.elements.is_empty() && zelf.class().is(vm.ctx.types.tuple_type) {
|
||||
if other.as_slice().is_empty() && zelf.class().is(vm.ctx.types.tuple_type) {
|
||||
zelf
|
||||
} else if zelf.elements.is_empty() && other.class().is(vm.ctx.types.tuple_type) {
|
||||
} else if zelf.as_slice().is_empty() && other.class().is(vm.ctx.types.tuple_type) {
|
||||
other
|
||||
} else {
|
||||
let elements = zelf
|
||||
.as_slice()
|
||||
.iter()
|
||||
.chain(other.as_slice())
|
||||
.cloned()
|
||||
@@ -466,7 +455,7 @@ impl PyTuple {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn __len__(&self) -> usize {
|
||||
self.elements.len()
|
||||
self.as_slice().len()
|
||||
}
|
||||
|
||||
fn __mul__(zelf: PyRef<Self>, value: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
@@ -500,8 +489,8 @@ impl PyTuple {
|
||||
range: OptionalRangeArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let (start, stop) = range.saturate(self.len(), vm)?;
|
||||
for (index, element) in self.elements.iter().enumerate().take(stop).skip(start) {
|
||||
let (start, stop) = range.saturate(self.as_slice().len(), vm)?;
|
||||
for (index, element) in self.as_slice().iter().enumerate().take(stop).skip(start) {
|
||||
if vm.identical_or_equal(element, &needle)? {
|
||||
return Ok(index);
|
||||
}
|
||||
@@ -548,7 +537,9 @@ impl PyTuple {
|
||||
impl AsMapping for PyTuple {
|
||||
fn as_mapping() -> &'static PyMappingMethods {
|
||||
static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
|
||||
length: atomic_func!(|mapping, _vm| Ok(PyTuple::mapping_downcast(mapping).len())),
|
||||
length: atomic_func!(|mapping, _vm| {
|
||||
Ok(PyTuple::mapping_downcast(mapping).as_slice().len())
|
||||
}),
|
||||
subscript: atomic_func!(
|
||||
|mapping, needle, vm| PyTuple::mapping_downcast(mapping)._getitem(needle, vm)
|
||||
),
|
||||
@@ -595,7 +586,7 @@ impl AsNumber for PyTuple {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
boolean: Some(|number, _vm| {
|
||||
let zelf = number.obj.downcast_ref::<PyTuple>().unwrap();
|
||||
Ok(!zelf.elements.is_empty())
|
||||
Ok(!zelf.as_slice().is_empty())
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
@@ -621,9 +612,10 @@ impl Comparable for PyTuple {
|
||||
return Ok(res.into());
|
||||
}
|
||||
let other = class_or_notimplemented!(Self, other);
|
||||
zelf.iter()
|
||||
zelf.as_slice()
|
||||
.iter()
|
||||
.map(|o| &**o)
|
||||
.richcompare(other.iter().map(|o| &**o), op, vm)
|
||||
.richcompare(other.as_slice().iter().map(|o| &**o), op, vm)
|
||||
.map(PyComparisonValue::Implemented)
|
||||
}
|
||||
}
|
||||
@@ -640,13 +632,20 @@ impl Iterable for PyTuple {
|
||||
impl Representable for PyTuple {
|
||||
#[inline]
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
let s = if zelf.is_empty() {
|
||||
let s = if zelf.as_slice().is_empty() {
|
||||
vm.ctx.intern_str("()").to_owned()
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let s = if zelf.len() == 1 {
|
||||
wtf8_concat!("(", zelf.elements[0].repr(vm)?.as_wtf8(), ",)")
|
||||
let s = if zelf.as_slice().len() == 1 {
|
||||
wtf8_concat!("(", zelf.as_slice()[0].repr(vm)?.as_wtf8(), ",)")
|
||||
} else {
|
||||
collection_repr(None, "(", ")", "()", zelf.elements.iter().map(|o| &**o), vm)?
|
||||
collection_repr(
|
||||
None,
|
||||
"(",
|
||||
")",
|
||||
"()",
|
||||
zelf.as_slice().iter().map(|o| &**o),
|
||||
vm,
|
||||
)?
|
||||
};
|
||||
vm.ctx.new_str(s)
|
||||
} else {
|
||||
@@ -713,14 +712,14 @@ impl PyPayload for PyTupleIterator {
|
||||
impl PyTupleIterator {
|
||||
#[pymethod]
|
||||
fn __length_hint__(&self) -> usize {
|
||||
self.internal.lock().length_hint(|obj| obj.len())
|
||||
self.internal.lock().length_hint(|obj| obj.as_slice().len())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
self.internal
|
||||
.lock()
|
||||
.set_state(&state, |obj, pos| pos.min(obj.len()), vm)
|
||||
.set_state(&state, |obj, pos| pos.min(obj.as_slice().len()), vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
|
||||
@@ -797,7 +797,7 @@ impl PyType {
|
||||
interpreter_id: HeapTypeExt::creating_interpreter_id(),
|
||||
};
|
||||
let bases = PyTuple::new_ref_typed(bases, ctx);
|
||||
let base = bases[0].clone();
|
||||
let base = bases.as_slice()[0].clone();
|
||||
|
||||
Self::new_heap_inner(
|
||||
base,
|
||||
@@ -1651,6 +1651,26 @@ impl PyType {
|
||||
}
|
||||
|
||||
impl Py<PyType> {
|
||||
#[inline]
|
||||
pub fn slots(&self) -> &PyTypeSlots {
|
||||
&self.payload().slots
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn attributes(&self) -> &TypeNamespace {
|
||||
&self.payload().attributes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tp_version_tag(&self) -> &AtomicU32 {
|
||||
&self.payload().tp_version_tag
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn heaptype_ext(&self) -> Option<&HeapTypeExt> {
|
||||
self.payload().heaptype_ext.as_deref()
|
||||
}
|
||||
|
||||
pub fn is_subtype(&self, other: &Self) -> bool {
|
||||
is_subtype_with_mro(&self.mro.read(), self, other)
|
||||
}
|
||||
@@ -1722,7 +1742,7 @@ impl PyType {
|
||||
fn set_bases(zelf: &Py<Self>, bases_tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// TODO: Assigning to __bases__ is only used in typing.NamedTupleMeta.__new__
|
||||
// Rather than correctly re-initializing the class, we are skipping a few steps for now
|
||||
if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"cannot set '__bases__' attribute of immutable type '{}'",
|
||||
zelf.name()
|
||||
@@ -3015,7 +3035,7 @@ impl GetAttr for PyType {
|
||||
let zelf_attr = zelf.get_attr(name);
|
||||
|
||||
if let Some(attr) = zelf_attr {
|
||||
let descr_get = attr.class().slots.descr_get.load();
|
||||
let descr_get = attr.class().slots().descr_get.load();
|
||||
if let Some(descr_get) = descr_get {
|
||||
descr_get(attr.as_object(), None, Some(zelf.as_object()), vm)
|
||||
} else {
|
||||
@@ -3058,7 +3078,7 @@ impl Py<PyType> {
|
||||
// CPython returns None if __doc__ is not in the type's own dict
|
||||
if let Some(doc_attr) = self.get_direct_attr(vm.ctx.intern_str("__doc__")) {
|
||||
// If it's a descriptor, call its __get__ method
|
||||
let descr_get = doc_attr.class().slots.descr_get.load();
|
||||
let descr_get = doc_attr.class().slots().descr_get.load();
|
||||
if let Some(descr_get) = descr_get {
|
||||
descr_get(doc_attr.as_object(), None, Some(self.as_object()), vm)
|
||||
} else {
|
||||
@@ -3135,7 +3155,7 @@ impl SetAttr for PyType {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let attr_name = vm.ctx.intern_str(attr_name.as_wtf8());
|
||||
if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"cannot set '{}' attribute of immutable type '{}'",
|
||||
attr_name,
|
||||
@@ -3143,7 +3163,7 @@ impl SetAttr for PyType {
|
||||
)));
|
||||
}
|
||||
if let Some(attr) = zelf.get_class_attr(attr_name) {
|
||||
let descr_set = attr.class().slots.descr_set.load();
|
||||
let descr_set = attr.class().slots().descr_set.load();
|
||||
if let Some(descriptor) = descr_set {
|
||||
return descriptor(&attr, zelf.to_owned().into(), value, vm);
|
||||
}
|
||||
@@ -3160,9 +3180,9 @@ impl SetAttr for PyType {
|
||||
zelf.modified_inner();
|
||||
|
||||
let prev_value = if let PySetterValue::Assign(value) = value {
|
||||
zelf.attributes.insert(attr_name, value)
|
||||
zelf.attributes().insert(attr_name, value)
|
||||
} else {
|
||||
let prev_value = zelf.attributes.remove(attr_name);
|
||||
let prev_value = zelf.attributes().remove(attr_name);
|
||||
if prev_value.is_none() {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
@@ -3203,8 +3223,10 @@ impl Callable for PyType {
|
||||
}
|
||||
}
|
||||
|
||||
let Some(slot_new) = zelf.slots.new.load() else {
|
||||
return Err(vm.new_type_error(format!("cannot create '{}' instances", zelf.slots.name)));
|
||||
let Some(slot_new) = zelf.slots().new.load() else {
|
||||
return Err(
|
||||
vm.new_type_error(format!("cannot create '{}' instances", zelf.slots().name))
|
||||
);
|
||||
};
|
||||
|
||||
// Both the new and init slots consume args, so the init call gets a
|
||||
@@ -3223,7 +3245,7 @@ impl Callable for PyType {
|
||||
// so a mismatch is conservative: if it ever compared unequal for the
|
||||
// wrapper it would only take the slower cloning path, never the fast
|
||||
// path incorrectly.
|
||||
if zelf.slots.init.load().is_none()
|
||||
if zelf.slots().init.load().is_none()
|
||||
&& !zelf.is(vm.ctx.types.type_type)
|
||||
&& crate::types::fn_addr(slot_new)
|
||||
!= crate::types::fn_addr(crate::types::new_wrapper as crate::types::NewFunc)
|
||||
@@ -3239,7 +3261,7 @@ impl Callable for PyType {
|
||||
return Ok(obj);
|
||||
}
|
||||
|
||||
if let Some(init_method) = obj.class().slots.init.load() {
|
||||
if let Some(init_method) = obj.class().slots().init.load() {
|
||||
init_method(&obj, init_args, vm)?;
|
||||
}
|
||||
Ok(obj)
|
||||
@@ -3406,10 +3428,10 @@ fn vectorcall_type(
|
||||
if nargs == 1 && no_kwargs {
|
||||
return Ok(args[0].obj_type());
|
||||
}
|
||||
} else if zelf.slots.call.load().is_none() && zelf.slots.new.load().is_some() {
|
||||
} else if zelf.slots().call.load().is_none() && zelf.slots().new.load().is_some() {
|
||||
// Per-type constructor vectorcall for non-callable types (dict, list, int, etc.)
|
||||
// Also guard on slots.new to avoid dispatching for DISALLOW_INSTANTIATION types.
|
||||
if let Some(type_vc) = zelf.slots.vectorcall.load() {
|
||||
if let Some(type_vc) = zelf.slots().vectorcall.load() {
|
||||
return type_vc(zelf_obj, args, nargs, kwnames, vm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ fn dedup_and_flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult<U
|
||||
pub fn make_union(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult {
|
||||
let result = dedup_and_flatten_args(args, vm)?;
|
||||
Ok(match result.args.len() {
|
||||
1 => result.args[0].to_owned(),
|
||||
1 => result.args.as_slice()[0].to_owned(),
|
||||
_ => PyUnion::from_components(result, vm)?.to_pyobject(vm),
|
||||
})
|
||||
}
|
||||
@@ -383,7 +383,7 @@ impl PyUnion {
|
||||
Ok(if new_args.is_empty() {
|
||||
make_union(&new_args, vm)?
|
||||
} else {
|
||||
let mut tmp = new_args[0].to_owned();
|
||||
let mut tmp = new_args.as_slice()[0].to_owned();
|
||||
for arg in new_args.iter().skip(1) {
|
||||
tmp = vm._or(&tmp, arg)?;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ impl PyPayload for PyWeakProxy {
|
||||
|
||||
#[inline]
|
||||
unsafe fn validate_downcastable_from(obj: &PyObject) -> bool {
|
||||
<Self as ::rustpython_vm::class::PyClassDef>::BASICSIZE <= obj.class().slots.basicsize
|
||||
<Self as ::rustpython_vm::class::PyClassDef>::BASICSIZE <= obj.class().slots().basicsize
|
||||
&& obj
|
||||
.class()
|
||||
.fast_issubclass(<Self as ::rustpython_vm::class::StaticType>::static_type())
|
||||
@@ -170,7 +170,7 @@ impl Iterable for PyWeakProxy {
|
||||
impl IterNext for PyWeakProxy {
|
||||
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
|
||||
let obj = zelf.try_upgrade(vm)?;
|
||||
if obj.class().slots.iternext.load().is_none() {
|
||||
if obj.class().slots().iternext.load().is_none() {
|
||||
return Err(vm.new_type_error("Weakref proxy referenced a non-iterator"));
|
||||
}
|
||||
PyIter::new(obj).next(vm)
|
||||
|
||||
@@ -57,7 +57,7 @@ fn collect_bytes(
|
||||
// which knows what it was being asked to do, rather than by the
|
||||
// iteration protocol saying the object is not iterable.
|
||||
let cls = obj.class();
|
||||
if cls.slots.iter.load().is_none() && !cls.has_attr(identifier!(vm, __getitem__)) {
|
||||
if cls.slots().iter.load().is_none() && !cls.has_attr(identifier!(vm, __getitem__)) {
|
||||
return Err(vm.new_type_error(unusable(&cls.name())));
|
||||
}
|
||||
let value = |x: PyObjectRef| value_from_object_with(vm, &x, element);
|
||||
|
||||
@@ -138,7 +138,7 @@ fn spec_format_bytes(
|
||||
// CPython parity: bytes `%c` accepts a single byte or any object
|
||||
// with `__index__` in range(256).
|
||||
if let Some(b) = obj.downcast_ref::<PyBytes>() {
|
||||
if b.len() == 1 {
|
||||
if b.as_bytes().len() == 1 {
|
||||
return Ok(spec.format_char(b.as_bytes()[0]));
|
||||
}
|
||||
} else if let Some(ba) = obj.downcast_ref::<PyByteArray>() {
|
||||
@@ -155,7 +155,7 @@ fn spec_format_bytes(
|
||||
// A bytes-like argument that is not one byte long is named by
|
||||
// its length rather than by its type.
|
||||
let what = if let Some(b) = obj.downcast_ref::<PyBytes>() {
|
||||
format!("a bytes object of length {}", b.len())
|
||||
format!("a bytes object of length {}", b.as_bytes().len())
|
||||
} else if let Some(ba) = obj.downcast_ref::<PyByteArray>() {
|
||||
format!("a bytearray object of length {}", ba.borrow_buf().len())
|
||||
} else {
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
|
||||
|
||||
// Special handling for __hash__ = None
|
||||
if def.name == "__hash__"
|
||||
&& class.slots.hash.load().is_some_and(|h| {
|
||||
&& class.slots().hash.load().is_some_and(|h| {
|
||||
fn_addr(h) == fn_addr(hash_not_implemented as crate::types::HashFunc)
|
||||
})
|
||||
{
|
||||
@@ -40,13 +40,13 @@ pub fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
|
||||
}
|
||||
|
||||
// Get the slot function wrapped in SlotFunc
|
||||
let Some(slot_func) = def.accessor.get_slot_func_with_op(&class.slots, def.op) else {
|
||||
let Some(slot_func) = def.accessor.get_slot_func_with_op(class.slots(), def.op) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Check if attribute already exists in dict
|
||||
let attr_name = ctx.intern_str(def.name);
|
||||
if class.attributes.contains(attr_name) {
|
||||
if class.attributes().contains(attr_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
{
|
||||
// NOTE: `is_created_with_flags` if only available when debug_assertions is true
|
||||
#[cfg(debug_assertions)]
|
||||
debug_assert!(class.slots.flags.is_created_with_flags());
|
||||
debug_assert!(class.slots().flags.is_created_with_flags());
|
||||
|
||||
let _ = ctx.intern_str(Self::NAME); // intern type name
|
||||
|
||||
@@ -181,7 +181,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
// Only set __doc__ if it doesn't already exist (e.g., as a member descriptor)
|
||||
// This matches CPython's behavior in type_dict_set_doc
|
||||
let doc_attr_name = identifier!(ctx, __doc__);
|
||||
if class.attributes.get(doc_attr_name).is_none() {
|
||||
if class.attributes().get(doc_attr_name).is_none() {
|
||||
class.set_attr(doc_attr_name, ctx.new_str(doc).into());
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
// Don't overwrite a getset descriptor for __module__ (e.g. TypeAliasType
|
||||
// has an instance-level __module__ getset that should not be replaced)
|
||||
let has_getset = class
|
||||
.attributes
|
||||
.attributes()
|
||||
.get(module_key)
|
||||
.is_some_and(|v| v.downcastable::<crate::builtins::PyGetSet>());
|
||||
if !has_getset {
|
||||
@@ -202,8 +202,8 @@ pub trait PyClassImpl: PyClassDef {
|
||||
// Don't add __new__ attribute if slot_new is inherited from object
|
||||
// (Python doesn't add __new__ to __dict__ for inherited slots)
|
||||
// Exception: object itself should have __new__ in its dict
|
||||
if let Some(slot_new) = class.slots.new.load() {
|
||||
let object_new = ctx.types.object_type.slots.new.load();
|
||||
if let Some(slot_new) = class.slots().new.load() {
|
||||
let object_new = ctx.types.object_type.slots().new.load();
|
||||
let is_object_itself = core::ptr::eq(class, ctx.types.object_type);
|
||||
let is_inherited_from_object = !is_object_itself
|
||||
&& object_new.is_some_and(|obj_new| fn_addr(slot_new) == fn_addr(obj_new));
|
||||
@@ -224,7 +224,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
class.inherit_slots(base);
|
||||
}
|
||||
|
||||
class.extend_methods(class.slots.methods, ctx);
|
||||
class.extend_methods(class.slots().methods, ctx);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -655,7 +655,7 @@ pub(crate) fn unraisable_while_closing(
|
||||
) {
|
||||
// Explicit close() leaves the traceback to the caller frame.
|
||||
// Finalize has no caller frame, so attach the generator site here.
|
||||
if e.__traceback__().is_none()
|
||||
if e.traceback().is_none()
|
||||
&& let Some(frame) = coro.frame_opt()
|
||||
{
|
||||
let lasti = frame.lasti().saturating_mul(2);
|
||||
|
||||
@@ -358,11 +358,11 @@ impl VirtualMachine {
|
||||
0 => vec![],
|
||||
1 => {
|
||||
let args0_repr = if str_single {
|
||||
varargs[0]
|
||||
varargs.as_slice()[0]
|
||||
.str(vm)
|
||||
.unwrap_or_else(|_| PyStr::from("<element str() failed>").into_ref(&vm.ctx))
|
||||
} else {
|
||||
varargs[0].repr(vm).unwrap_or_else(|_| {
|
||||
varargs.as_slice()[0].repr(vm).unwrap_or_else(|_| {
|
||||
PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
|
||||
})
|
||||
};
|
||||
@@ -383,7 +383,7 @@ impl VirtualMachine {
|
||||
&self,
|
||||
exc: PyBaseExceptionRef,
|
||||
) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
|
||||
let tb = exc.__traceback__().to_pyobject(self);
|
||||
let tb = exc.traceback().to_pyobject(self);
|
||||
let class = exc.class().to_owned();
|
||||
(class.into(), exc.into(), tb)
|
||||
}
|
||||
@@ -777,6 +777,11 @@ impl PyBaseException {
|
||||
|
||||
#[pyclass]
|
||||
impl Py<PyBaseException> {
|
||||
#[inline]
|
||||
pub fn traceback(&self) -> Option<PyTracebackRef> {
|
||||
self.payload().__traceback__()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyStrRef {
|
||||
let str_args = vm.exception_args_as_string(&self.args(), true);
|
||||
@@ -1249,7 +1254,7 @@ impl serde::Serialize for SerializeException<'_, '_> {
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
self.exc.__traceback__().map(Tracebacks)
|
||||
self.exc.traceback().map(Tracebacks)
|
||||
};
|
||||
struc.serialize_field("traceback", &tbs)?;
|
||||
struc.serialize_field(
|
||||
@@ -3413,7 +3418,7 @@ pub fn exception_group_match(
|
||||
let wrapped = eg_type.call((vm.ctx.new_str(""), excs), vm)?;
|
||||
// Copy traceback from original exception
|
||||
if let Ok(exc) = exc_value.to_owned().downcast::<types::PyBaseException>()
|
||||
&& let Some(tb) = exc.__traceback__()
|
||||
&& let Some(tb) = exc.traceback()
|
||||
&& let Ok(wrapped_exc) = wrapped.clone().downcast::<types::PyBaseException>()
|
||||
{
|
||||
wrapped_exc.set_traceback(Some(tb));
|
||||
|
||||
+61
-55
@@ -2670,7 +2670,7 @@ pub(crate) fn trampoline_handle_exception(
|
||||
|
||||
// Add traceback entry at the call site.
|
||||
if let Some((loc, _end_loc)) = exec.code.locations.get(idx) {
|
||||
let next = exception.__traceback__();
|
||||
let next = exception.traceback();
|
||||
let new_traceback = PyTraceback::new(next, exec.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
}
|
||||
@@ -3513,7 +3513,7 @@ impl ExecutingFrame<'_> {
|
||||
let exc_type: PyObjectRef = exc.class().to_owned().into();
|
||||
let exc_value: PyObjectRef = exc.to_owned().into();
|
||||
let exc_tb: PyObjectRef = exc
|
||||
.__traceback__()
|
||||
.traceback()
|
||||
.map_or_else(|| vm.ctx.none(), |tb| -> PyObjectRef { tb.into() });
|
||||
let tuple = vm.ctx.new_tuple(vec![exc_type, exc_value, exc_tb]).into();
|
||||
vm.trace_event(crate::protocol::TraceEvent::Exception, Some(tuple))?;
|
||||
@@ -3588,7 +3588,7 @@ impl ExecutingFrame<'_> {
|
||||
Ok(_) => {}
|
||||
Err(exception) => {
|
||||
if let Some((loc, _end_loc)) = self.code.locations.get(idx) {
|
||||
let next = exception.__traceback__();
|
||||
let next = exception.traceback();
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
self.frame_object(vm),
|
||||
@@ -3697,7 +3697,7 @@ impl ExecutingFrame<'_> {
|
||||
vm: &VirtualMachine,
|
||||
) -> FrameResult {
|
||||
if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
|
||||
let next = exception.__traceback__();
|
||||
let next = exception.traceback();
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
frame.frame_object(vm),
|
||||
@@ -3780,14 +3780,14 @@ impl ExecutingFrame<'_> {
|
||||
// Check if the exception already has traceback entries before
|
||||
// we add ours. If it does, it was propagated from a callee
|
||||
// function and we should not re-contextualize it.
|
||||
let had_prior_traceback = exception.__traceback__().is_some();
|
||||
let had_prior_traceback = exception.traceback().is_some();
|
||||
|
||||
// PyTraceBack_Here always adds a new entry without
|
||||
// checking for duplicates. Each time an exception passes through
|
||||
// a frame (e.g., in a loop with repeated raise statements),
|
||||
// a new traceback entry is added.
|
||||
if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
|
||||
let next = exception.__traceback__();
|
||||
let next = exception.traceback();
|
||||
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
@@ -3999,7 +3999,7 @@ impl ExecutingFrame<'_> {
|
||||
let idx = self.lasti().saturating_sub(1) as usize;
|
||||
if idx < self.code.locations.len() {
|
||||
let (loc, _end_loc) = self.code.locations[idx];
|
||||
let next = err.__traceback__();
|
||||
let next = err.traceback();
|
||||
let new_traceback =
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
err.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
@@ -4057,7 +4057,7 @@ impl ExecutingFrame<'_> {
|
||||
let idx = self.lasti().saturating_sub(1) as usize;
|
||||
if idx < self.code.locations.len() {
|
||||
let (loc, _end_loc) = self.code.locations[idx];
|
||||
let next = err.__traceback__();
|
||||
let next = err.traceback();
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
self.frame_object(vm),
|
||||
@@ -4106,7 +4106,7 @@ impl ExecutingFrame<'_> {
|
||||
let idx = self.lasti().saturating_sub(1) as usize;
|
||||
if idx < self.code.locations.len() {
|
||||
let (loc, _end_loc) = self.code.locations[idx];
|
||||
let next = exception.__traceback__();
|
||||
let next = exception.traceback();
|
||||
let new_traceback =
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
@@ -4907,7 +4907,7 @@ impl ExecutingFrame<'_> {
|
||||
// Only rewrite the error if the type is truly not iterable
|
||||
// (no __iter__ and no __getitem__). Preserve original TypeError
|
||||
// from custom iterables that raise during iteration.
|
||||
let not_iterable = iterable.class().slots.iter.load().is_none()
|
||||
let not_iterable = iterable.class().slots().iter.load().is_none()
|
||||
&& iterable
|
||||
.get_class_attr(vm.ctx.intern_str("__getitem__"))
|
||||
.is_none();
|
||||
@@ -6004,7 +6004,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
|
||||
{
|
||||
let owner = self.pop_stackref();
|
||||
@@ -6023,7 +6023,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& !owner.has_instance_dict()
|
||||
&& let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
|
||||
{
|
||||
@@ -6043,7 +6043,8 @@ impl ExecutingFrame<'_> {
|
||||
let owner = self.top_value();
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
|
||||
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
{
|
||||
// Check instance dict doesn't shadow the method.
|
||||
let shadowed = match self.shadowing_instance_attr(cache_base, attr_name, vm) {
|
||||
Ok(shadowed) => shadowed.is_some(),
|
||||
@@ -6071,7 +6072,8 @@ impl ExecutingFrame<'_> {
|
||||
let owner = self.top_value();
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
|
||||
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
{
|
||||
// Type version matches — no data descriptor for this attr.
|
||||
// Try direct dict lookup, skipping full descriptor protocol.
|
||||
if let Some(dict) = owner.dict()
|
||||
@@ -6094,7 +6096,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(dict) = owner.dict()
|
||||
{
|
||||
// Try the cached entry index first; a hit is an identity
|
||||
@@ -6132,7 +6134,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(module) = owner.downcast_ref_if_exact::<PyModule>(vm)
|
||||
&& let Ok(value) = module.get_attr(attr_name, vm)
|
||||
{
|
||||
@@ -6155,7 +6157,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
|
||||
{
|
||||
self.pop_stackref();
|
||||
@@ -6177,7 +6179,8 @@ impl ExecutingFrame<'_> {
|
||||
let owner = self.top_value();
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
|
||||
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
{
|
||||
// Instance dict has priority — check if attr is shadowed
|
||||
if let Some(value) = self.shadowing_instance_attr(cache_base, attr_name, vm)? {
|
||||
self.pop_stackref();
|
||||
@@ -6240,7 +6243,7 @@ impl ExecutingFrame<'_> {
|
||||
&& metaclass_version != 0
|
||||
&& let Some(owner_type) = owner.downcast_ref::<PyType>()
|
||||
&& owner_type.tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag.load(Acquire) == metaclass_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == metaclass_version
|
||||
&& let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
|
||||
{
|
||||
self.pop_stackref();
|
||||
@@ -6265,7 +6268,7 @@ impl ExecutingFrame<'_> {
|
||||
&& !self.specialization_eval_frame_active(vm)
|
||||
&& type_version != 0
|
||||
&& func_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(func_obj) =
|
||||
self.try_read_cached_descriptor(cache_base, type_version)
|
||||
&& let Some(func) = func_obj.downcast_ref_if_exact::<PyFunction>(vm)
|
||||
@@ -6289,7 +6292,8 @@ impl ExecutingFrame<'_> {
|
||||
let owner = self.top_value();
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
|
||||
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
{
|
||||
let slot_offset =
|
||||
self.code.instructions.read_cache_u32(cache_base + 3) as i32 as isize;
|
||||
if let Some(value) = owner.get_slot(slot_offset) {
|
||||
@@ -6315,7 +6319,7 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
if type_version != 0
|
||||
&& !self.specialization_eval_frame_active(vm)
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(fget_obj) =
|
||||
self.try_read_cached_descriptor(cache_base, type_version)
|
||||
&& let Some(func) = fget_obj.downcast_ref_if_exact::<PyFunction>(vm)
|
||||
@@ -6338,7 +6342,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(dict) = owner.dict()
|
||||
{
|
||||
self.pop_stackref(); // owner
|
||||
@@ -6360,7 +6364,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
|
||||
if type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some(dict) = owner.dict()
|
||||
{
|
||||
self.pop_stackref(); // owner
|
||||
@@ -6376,7 +6380,7 @@ impl ExecutingFrame<'_> {
|
||||
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
let version_match = type_version != 0 && {
|
||||
let owner = self.top_value();
|
||||
owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
};
|
||||
|
||||
if version_match {
|
||||
@@ -6462,7 +6466,7 @@ impl ExecutingFrame<'_> {
|
||||
let owner = self.nth_value(1);
|
||||
if !self.specialization_eval_frame_active(vm)
|
||||
&& type_version != 0
|
||||
&& owner.class().tp_version_tag.load(Acquire) == type_version
|
||||
&& owner.class().tp_version_tag().load(Acquire) == type_version
|
||||
&& let Some((func, func_version)) =
|
||||
owner.class().get_cached_getitem_for_specialization()
|
||||
&& func.func_version() == func_version
|
||||
@@ -7128,7 +7132,7 @@ impl ExecutingFrame<'_> {
|
||||
let nargs: u32 = arg.into();
|
||||
let callable = self.nth_value(nargs + 1);
|
||||
if let Some(cls) = callable.downcast_ref::<PyType>()
|
||||
&& cls.slots.vectorcall.load().is_some()
|
||||
&& cls.slots().vectorcall.load().is_some()
|
||||
{
|
||||
let (callable, args_vec) = self.take_call_args(nargs as usize);
|
||||
let effective_nargs = args_vec.len();
|
||||
@@ -7153,12 +7157,12 @@ impl ExecutingFrame<'_> {
|
||||
&& !self_or_null_is_some
|
||||
&& cached_version != 0
|
||||
&& let Some(cls) = callable.downcast_ref::<PyType>()
|
||||
&& cls.tp_version_tag.load(Acquire) == cached_version
|
||||
&& cls.tp_version_tag().load(Acquire) == cached_version
|
||||
&& let Some((init_func, init_func_version)) =
|
||||
cls.get_cached_init_for_specialization(cached_version)
|
||||
&& init_func.func_version() == init_func_version
|
||||
&& init_func.has_exact_argcount(nargs + 1)
|
||||
&& let Some(cls_alloc) = cls.slots.alloc.load()
|
||||
&& let Some(cls_alloc) = cls.slots().alloc.load()
|
||||
{
|
||||
// The specialization runs `__init__` directly with no
|
||||
// interpreter-visible trampoline frame. Deopt when the
|
||||
@@ -7510,14 +7514,14 @@ impl ExecutingFrame<'_> {
|
||||
if let Some(descr) = cls.get_direct_attr(attr_name) {
|
||||
let descr_cls = descr.class();
|
||||
if descr_cls
|
||||
.slots
|
||||
.slots()
|
||||
.flags
|
||||
.has_feature(PyTypeFlags::METHOD_DESCRIPTOR)
|
||||
{
|
||||
// Method descriptor: push unbound func + self
|
||||
// CALL will prepend self as first positional arg
|
||||
found = Some((descr, true));
|
||||
} else if let Some(descr_get) = descr_cls.slots.descr_get.load() {
|
||||
} else if let Some(descr_get) = descr_cls.slots().descr_get.load() {
|
||||
// Has __get__ but not METHOD_DESCRIPTOR: bind it
|
||||
let bound = descr_get(
|
||||
&descr,
|
||||
@@ -7695,7 +7699,8 @@ impl ExecutingFrame<'_> {
|
||||
let cache_base = instr_idx + 1;
|
||||
let obj = self.top_value();
|
||||
let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
|
||||
if cached_version != 0 && obj.class().tp_version_tag.load(Acquire) == cached_version
|
||||
if cached_version != 0
|
||||
&& obj.class().tp_version_tag().load(Acquire) == cached_version
|
||||
{
|
||||
self.pop_stackref();
|
||||
self.push_bool_or_fused_jump(instruction.cache_entries(), true, vm);
|
||||
@@ -8775,7 +8780,7 @@ impl ExecutingFrame<'_> {
|
||||
// Stack: [callable, self_or_null]
|
||||
let callable = self.nth_value(1);
|
||||
let func_str = Self::object_function_str(callable, vm);
|
||||
let not_iterable = args_obj.class().slots.iter.load().is_none()
|
||||
let not_iterable = args_obj.class().slots().iter.load().is_none()
|
||||
&& args_obj
|
||||
.get_class_attr(vm.ctx.intern_str("__getitem__"))
|
||||
.is_none();
|
||||
@@ -9157,7 +9162,7 @@ impl ExecutingFrame<'_> {
|
||||
fn execute_unpack_ex(&mut self, vm: &VirtualMachine, before: u8, after: u32) -> FrameResult {
|
||||
let (before, after) = (before as usize, after as usize);
|
||||
let value = self.pop_value();
|
||||
let not_iterable = value.class().slots.iter.load().is_none()
|
||||
let not_iterable = value.class().slots().iter.load().is_none()
|
||||
&& value
|
||||
.get_class_attr(vm.ctx.intern_str("__getitem__"))
|
||||
.is_none();
|
||||
@@ -9686,7 +9691,7 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
// General path — iterate up to `size + 1` elements to avoid
|
||||
// consuming the entire iterator (fixes hang on infinite sequences).
|
||||
let not_iterable = value.class().slots.iter.load().is_none()
|
||||
let not_iterable = value.class().slots().iter.load().is_none()
|
||||
&& value
|
||||
.get_class_attr(vm.ctx.intern_str("__getitem__"))
|
||||
.is_none();
|
||||
@@ -10073,7 +10078,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
// Only specialize if getattro is the default (PyBaseObject::getattro)
|
||||
let is_default_getattro = cls.slots.getattro.load().is_some_and(|f| {
|
||||
let is_default_getattro = cls.slots().getattro.load().is_some_and(|f| {
|
||||
crate::types::fn_addr(f)
|
||||
== crate::types::fn_addr(PyBaseObject::getattro as crate::types::GetattroFunc)
|
||||
});
|
||||
@@ -10147,7 +10152,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
let cls_attr = cls.get_attr(attr_name);
|
||||
let class_has_dict = cls.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
|
||||
let class_has_dict = cls.slots().flags.has_feature(PyTypeFlags::HAS_DICT);
|
||||
|
||||
if oparg.is_method() {
|
||||
// Method specialization
|
||||
@@ -10186,12 +10191,12 @@ impl ExecutingFrame<'_> {
|
||||
// Regular attribute access
|
||||
let has_data_descr = cls_attr.as_ref().is_some_and(|descr| {
|
||||
let descr_cls = descr.class();
|
||||
descr_cls.slots.descr_get.load().is_some()
|
||||
&& descr_cls.slots.descr_set.load().is_some()
|
||||
descr_cls.slots().descr_get.load().is_some()
|
||||
&& descr_cls.slots().descr_set.load().is_some()
|
||||
});
|
||||
let has_descr_get = cls_attr
|
||||
.as_ref()
|
||||
.is_some_and(|descr| descr.class().slots.descr_get.load().is_some());
|
||||
.is_some_and(|descr| descr.class().slots().descr_get.load().is_some());
|
||||
|
||||
if has_data_descr {
|
||||
// Check for member descriptor (slot access)
|
||||
@@ -10332,7 +10337,7 @@ impl ExecutingFrame<'_> {
|
||||
let (mcl_attr, mut metaclass_version) = mcl.lookup_ref_and_version_interned(attr_name, _vm);
|
||||
if let Some(ref attr) = mcl_attr {
|
||||
let attr_class = attr.class();
|
||||
if attr_class.slots.descr_set.load().is_some() {
|
||||
if attr_class.slots().descr_set.load().is_some() {
|
||||
// Data descriptor on metaclass — can't specialize
|
||||
unsafe {
|
||||
self.code.instructions.write_adaptive_counter(
|
||||
@@ -10549,7 +10554,7 @@ impl ExecutingFrame<'_> {
|
||||
let cls = a.class();
|
||||
// Check the cheap gates before the __getitem__ lookup, which
|
||||
// takes the global type lock and may allocate a version tag.
|
||||
if cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
&& !self.specialization_eval_frame_active(vm)
|
||||
{
|
||||
let (getitem, type_version) =
|
||||
@@ -11058,7 +11063,7 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
// type/str/tuple(x) and class-call specializations
|
||||
if let Some(cls) = callable.downcast_ref::<PyType>() {
|
||||
if cls.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
if cls.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
if !self_or_null_is_some && nargs == 1 {
|
||||
let new_op = if callable.is(&vm.ctx.types.type_type.as_object()) {
|
||||
Some(Instruction::CallType1)
|
||||
@@ -11074,7 +11079,7 @@ impl ExecutingFrame<'_> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if cls.slots.vectorcall.load().is_some() {
|
||||
if cls.slots().vectorcall.load().is_some() {
|
||||
self.specialize_at(instr_idx, cache_base, Instruction::CallBuiltinClass);
|
||||
return;
|
||||
}
|
||||
@@ -11090,15 +11095,15 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
// CallAllocAndEnterInit: heap type with default __new__
|
||||
if !self_or_null_is_some && cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
if !self_or_null_is_some && cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
// Capture the version before inspecting tp_new/tp_alloc so a
|
||||
// concurrently installed __new__ invalidates the version this
|
||||
// specialization is cached against.
|
||||
let type_version = cls.version_for_specialization(vm);
|
||||
let object_new = vm.ctx.types.object_type.slots.new.load();
|
||||
let cls_new = cls.slots.new.load();
|
||||
let object_alloc = vm.ctx.types.object_type.slots.alloc.load();
|
||||
let cls_alloc = cls.slots.alloc.load();
|
||||
let object_new = vm.ctx.types.object_type.slots().new.load();
|
||||
let cls_new = cls.slots().new.load();
|
||||
let object_alloc = vm.ctx.types.object_type.slots().alloc.load();
|
||||
let cls_alloc = cls.slots().alloc.load();
|
||||
if let (Some(cls_new_fn), Some(obj_new_fn), Some(cls_alloc_fn), Some(obj_alloc_fn)) =
|
||||
(cls_new, object_new, cls_alloc, object_alloc)
|
||||
&& crate::types::fn_addr(cls_new_fn) == crate::types::fn_addr(obj_new_fn)
|
||||
@@ -11502,14 +11507,14 @@ impl ExecutingFrame<'_> {
|
||||
Some(Instruction::ToBoolList)
|
||||
} else if cls.is(PyStr::class(&vm.ctx)) {
|
||||
Some(Instruction::ToBoolStr)
|
||||
} else if cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
} else if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
// Capture the version before inspecting the bool/len slots so a
|
||||
// concurrently installed __bool__/__len__ invalidates the version
|
||||
// the ToBoolAlwaysTrue guard is cached against.
|
||||
let type_version = cls.version_for_specialization(vm);
|
||||
let has_bool_or_len = cls.slots.as_number.boolean.load().is_some()
|
||||
|| cls.slots.as_mapping.length.load().is_some()
|
||||
|| cls.slots.as_sequence.length.load().is_some();
|
||||
let has_bool_or_len = cls.slots().as_number.boolean.load().is_some()
|
||||
|| cls.slots().as_mapping.length.load().is_some()
|
||||
|| cls.slots().as_sequence.length.load().is_some();
|
||||
if !has_bool_or_len {
|
||||
if type_version != 0 {
|
||||
unsafe {
|
||||
@@ -11977,7 +11982,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
// Only specialize if setattr is the default (generic_setattr)
|
||||
let is_default_setattr = cls.slots.setattro.load().is_some_and(|f| {
|
||||
let is_default_setattr = cls.slots().setattro.load().is_some_and(|f| {
|
||||
crate::types::fn_addr(f)
|
||||
== crate::types::fn_addr(PyBaseObject::slot_setattro as crate::types::SetattroFunc)
|
||||
});
|
||||
@@ -11997,7 +12002,8 @@ impl ExecutingFrame<'_> {
|
||||
let cls_attr = cls.get_attr(attr_name);
|
||||
let has_data_descr = cls_attr.as_ref().is_some_and(|descr| {
|
||||
let descr_cls = descr.class();
|
||||
descr_cls.slots.descr_get.load().is_some() && descr_cls.slots.descr_set.load().is_some()
|
||||
descr_cls.slots().descr_get.load().is_some()
|
||||
&& descr_cls.slots().descr_set.load().is_some()
|
||||
});
|
||||
|
||||
if has_data_descr {
|
||||
|
||||
@@ -271,7 +271,7 @@ pub fn remove_importlib_frames(vm: &VirtualMachine, exc: &Py<PyBaseException>) {
|
||||
|
||||
let always_trim = exc.fast_isinstance(vm.ctx.exceptions.import_error);
|
||||
|
||||
if let Some(tb) = exc.__traceback__() {
|
||||
if let Some(tb) = exc.traceback() {
|
||||
let trimmed_tb = remove_importlib_frames_inner(vm, Some(tb), always_trim).0;
|
||||
exc.set_traceback(trimmed_tb);
|
||||
}
|
||||
|
||||
@@ -2110,7 +2110,7 @@ impl PyObject {
|
||||
// __del__ should only be called once (like _PyGC_FINALIZED check in GIL_DISABLED)
|
||||
// We call __del__ BEFORE clearing weakrefs to allow the finalizer to access
|
||||
// the object's weak references if needed.
|
||||
let del = self.class().slots.del.load();
|
||||
let del = self.class().slots().del.load();
|
||||
if let Some(slot_del) = del
|
||||
&& !self.gc_finalized()
|
||||
{
|
||||
@@ -2228,7 +2228,7 @@ impl PyObject {
|
||||
/// This allows proper resurrection detection.
|
||||
/// PyObject_CallFinalizerFromDealloc
|
||||
pub fn try_call_finalizer(&self) {
|
||||
let del = self.class().slots.del.load();
|
||||
let del = self.class().slots().del.load();
|
||||
if let Some(slot_del) = del
|
||||
&& !self.gc_finalized()
|
||||
&& self
|
||||
|
||||
@@ -312,7 +312,7 @@ impl PyBuffer {
|
||||
}
|
||||
// slot_bf_releasebuffer: a Python-level `__release_buffer__` runs first,
|
||||
// then the exporter's own release so export counts stay balanced.
|
||||
if self.obj.class().slots.python_release_buffer.load() {
|
||||
if self.obj.class().slots().python_release_buffer.load() {
|
||||
crate::builtins::memory::release_buffer_call_python(self);
|
||||
}
|
||||
(self.methods.release)(self)
|
||||
@@ -368,7 +368,7 @@ impl PyObject {
|
||||
/// `__buffer__` is not mistaken for "not a buffer".
|
||||
#[must_use]
|
||||
pub fn check_buffer(&self) -> bool {
|
||||
self.class().slots.as_buffer.load().is_some()
|
||||
self.class().slots().as_buffer.load().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ pub struct PyCallable<'a> {
|
||||
|
||||
impl<'a> PyCallable<'a> {
|
||||
pub fn new(obj: &'a PyObject) -> Option<Self> {
|
||||
let slots = &obj.class().slots;
|
||||
let slots = obj.class().slots();
|
||||
let call = slots.call.load()?;
|
||||
let vectorcall = slots.vectorcall.load();
|
||||
Some(PyCallable {
|
||||
|
||||
@@ -26,7 +26,7 @@ unsafe impl<O: Borrow<PyObject>> Traverse for PyIter<O> {
|
||||
|
||||
impl PyIter<PyObjectRef> {
|
||||
pub fn check(obj: &PyObject) -> bool {
|
||||
obj.class().slots.iternext.load().is_some()
|
||||
obj.class().slots().iternext.load().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ impl TryFromObject for PyIter<PyObjectRef> {
|
||||
// in the vm when a for loop is entered. Next, it is used when the builtin
|
||||
// function 'iter' is called.
|
||||
fn try_from_object(vm: &VirtualMachine, iter_target: PyObjectRef) -> PyResult<Self> {
|
||||
let get_iter = iter_target.class().slots.iter.load();
|
||||
let get_iter = iter_target.class().slots().iter.load();
|
||||
if let Some(get_iter) = get_iter {
|
||||
let iter = get_iter(iter_target, vm)?;
|
||||
if Self::check(&iter) {
|
||||
|
||||
@@ -113,7 +113,7 @@ impl PyMapping<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slots(&self) -> &PyMappingSlots {
|
||||
&self.obj.class().slots.as_mapping
|
||||
&self.obj.class().slots().as_mapping
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -661,12 +661,12 @@ impl PyNumber<'_> {
|
||||
// PyIndex_Check
|
||||
#[must_use]
|
||||
pub fn is_index(self) -> bool {
|
||||
self.class().slots.as_number.index.load().is_some()
|
||||
self.class().slots().as_number.index.load().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
|
||||
self.class().slots.as_number.int.load().map(|f| {
|
||||
self.class().slots().as_number.int.load().map(|f| {
|
||||
let ret = f(self, vm)?;
|
||||
|
||||
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
|
||||
@@ -695,7 +695,7 @@ and may be removed in a future version of Python.",
|
||||
|
||||
#[inline]
|
||||
pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
|
||||
self.class().slots.as_number.index.load().map(|f| {
|
||||
self.class().slots().as_number.index.load().map(|f| {
|
||||
let ret = f(self, vm)?;
|
||||
|
||||
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
|
||||
@@ -724,7 +724,7 @@ and may be removed in a future version of Python.",
|
||||
|
||||
#[inline]
|
||||
pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
|
||||
self.class().slots.as_number.float.load().map(|f| {
|
||||
self.class().slots().as_number.float.load().map(|f| {
|
||||
let ret = f(self, vm)?;
|
||||
|
||||
if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
|
||||
@@ -756,7 +756,7 @@ and may be removed in a future version of Python.",
|
||||
// PyNumber_Check - slots are now inherited
|
||||
#[must_use]
|
||||
pub fn check(obj: &PyObject) -> bool {
|
||||
let methods = &obj.class().slots.as_number;
|
||||
let methods = &obj.class().slots().as_number;
|
||||
let has_number = methods.int.load().is_some()
|
||||
|| methods.index.load().is_some()
|
||||
|| methods.float.load().is_some();
|
||||
|
||||
@@ -134,7 +134,7 @@ impl PyObject {
|
||||
#[inline]
|
||||
pub(crate) fn get_attr_inner(&self, attr_name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("object.__getattribute__: {:?} {:?}", self, attr_name);
|
||||
let getattro = self.class().slots.getattro.load().unwrap();
|
||||
let getattro = self.class().slots().getattro.load().unwrap();
|
||||
getattro(self, attr_name, vm).inspect_err(|exc| {
|
||||
vm.set_attribute_error_context(exc, self.to_owned(), attr_name.to_owned());
|
||||
})
|
||||
@@ -191,7 +191,7 @@ impl PyObject {
|
||||
.interned_str(attr_name)
|
||||
.and_then(|attr_name| self.get_class_attr(attr_name));
|
||||
if let Some(attr) = &descr
|
||||
&& let Some(descriptor) = attr.class().slots.descr_set.load()
|
||||
&& let Some(descriptor) = attr.class().slots().descr_set.load()
|
||||
{
|
||||
return descriptor(attr, self.to_owned(), value, vm);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ impl PyObject {
|
||||
}
|
||||
// Only a type that left __setattr__ alone can be told about the
|
||||
// missing __dict__, since overriding it is what hides the slot.
|
||||
let generic_setattro = self.class().slots.setattro.load().is_some_and(|f| {
|
||||
let generic_setattro = self.class().slots().setattro.load().is_some_and(|f| {
|
||||
crate::types::fn_addr(f)
|
||||
== crate::types::fn_addr(
|
||||
PyBaseObject::slot_setattro as crate::types::SetattroFunc,
|
||||
@@ -338,7 +338,7 @@ impl PyObject {
|
||||
) -> PyResult<Either<PyObjectRef, bool>> {
|
||||
let swapped = op.swapped();
|
||||
let call_cmp = |obj: &Self, other: &Self, op| {
|
||||
let Some(cmp) = obj.class().slots.richcompare.load() else {
|
||||
let Some(cmp) = obj.class().slots().richcompare.load() else {
|
||||
return Ok(PyArithmeticValue::NotImplemented);
|
||||
};
|
||||
let r = match cmp(obj, other, op, vm)? {
|
||||
@@ -420,7 +420,7 @@ impl PyObject {
|
||||
|
||||
pub fn repr(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
vm.with_recursion("while getting the repr of an object", || {
|
||||
self.class().slots.repr.load().map_or_else(
|
||||
self.class().slots().repr.load().map_or_else(
|
||||
|| {
|
||||
Err(vm.new_runtime_error(format!(
|
||||
"BUG: object of type '{}' has no __repr__ method. This is a bug in RustPython.",
|
||||
@@ -460,7 +460,7 @@ impl PyObject {
|
||||
Err(obj) => obj,
|
||||
};
|
||||
|
||||
// TODO: replace to obj.class().slots.str
|
||||
// TODO: replace to obj.class().slots().str
|
||||
let Some(str_method) = vm.get_special_method(&obj, identifier!(vm, __str__))? else {
|
||||
return obj.repr(vm);
|
||||
};
|
||||
@@ -720,7 +720,7 @@ impl PyObject {
|
||||
}
|
||||
|
||||
pub fn hash(&self, vm: &VirtualMachine) -> PyResult<PyHash> {
|
||||
if let Some(hash) = self.class().slots.hash.load() {
|
||||
if let Some(hash) = self.class().slots().hash.load() {
|
||||
return vm.with_recursion("while hashing", || hash(self, vm));
|
||||
}
|
||||
|
||||
@@ -837,7 +837,7 @@ impl PyObject {
|
||||
// A type carrying a sequence table turns the deletion down in
|
||||
// PySequence_DelItem's words instead; every heap type carries one.
|
||||
let name = self.class().slot_name();
|
||||
let msg = if seq.slots().has_any() || self.class().heaptype_ext.is_some() {
|
||||
let msg = if seq.slots().has_any() || self.class().heaptype_ext().is_some() {
|
||||
format!("'{name}' object doesn't support item deletion")
|
||||
} else {
|
||||
format!("'{name}' object does not support item deletion")
|
||||
@@ -859,7 +859,7 @@ impl PyObject {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let descr_get = res.class().slots.descr_get.load();
|
||||
let descr_get = res.class().slots().descr_get.load();
|
||||
if let Some(descr_get) = descr_get {
|
||||
descr_get(res.as_object(), Some(self), Some(obj_cls.as_object()), vm).map(Some)
|
||||
} else {
|
||||
|
||||
@@ -153,7 +153,7 @@ impl PySequence<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slots(&self) -> &PySequenceSlots {
|
||||
&self.obj.class().slots.as_sequence
|
||||
&self.obj.class().slots().as_sequence
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -287,7 +287,7 @@ mod _codecs {
|
||||
#[pyfunction]
|
||||
fn escape_encode(args: EscapeEncodeArgs, _vm: &VirtualMachine) -> (Vec<u8>, usize) {
|
||||
let encoded = encodings::escape::encode(args.data.as_bytes());
|
||||
(encoded, args.data.len())
|
||||
(encoded, args.data.as_bytes().len())
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
|
||||
@@ -751,8 +751,8 @@ impl Constructor for PyCFuncPtr {
|
||||
.ok_or_else(|| vm.new_type_error("Expected a string"))?
|
||||
.to_string();
|
||||
let dll = tuple
|
||||
.iter()
|
||||
.nth(1)
|
||||
.as_slice()
|
||||
.get(1)
|
||||
.ok_or_else(|| vm.new_type_error("Expected a tuple with at least 2 elements"))?
|
||||
.clone();
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul
|
||||
// c_set: accepts bytes(len=1), bytearray(len=1), or int(0-255)
|
||||
if value
|
||||
.downcast_ref_if_exact::<PyBytes>(vm)
|
||||
.is_some_and(|v| v.len() == 1)
|
||||
.is_some_and(|v| v.as_bytes().len() == 1)
|
||||
|| value
|
||||
.downcast_ref_if_exact::<PyByteArray>(vm)
|
||||
.is_some_and(|v| v.borrow_buf().len() == 1)
|
||||
@@ -303,7 +303,7 @@ impl PyCSimpleType {
|
||||
// c_char: 1 byte character
|
||||
Some("c") => {
|
||||
if let Some(bytes) = value.downcast_ref::<PyBytes>()
|
||||
&& bytes.len() == 1
|
||||
&& bytes.as_bytes().len() == 1
|
||||
{
|
||||
return create_simple_with_value("c", &value);
|
||||
}
|
||||
|
||||
@@ -634,7 +634,7 @@ impl SetAttr for PyCStructType {
|
||||
|
||||
// Check for data descriptor first
|
||||
if let Some(attr) = pytype.get_class_attr(attr_name_interned) {
|
||||
let descr_set = attr.class().slots.descr_set.load();
|
||||
let descr_set = attr.class().slots().descr_set.load();
|
||||
if let Some(descriptor) = descr_set {
|
||||
return descriptor(&attr, pytype.to_owned().into(), value, vm);
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ impl SetAttr for PyCUnionType {
|
||||
// 1. First, do PyType's setattro (PyType_Type.tp_setattro first)
|
||||
// Check for data descriptor first
|
||||
if let Some(attr) = pytype.get_class_attr(attr_name_interned) {
|
||||
let descr_set = attr.class().slots.descr_set.load();
|
||||
let descr_set = attr.class().slots().descr_set.load();
|
||||
if let Some(descriptor) = descr_set {
|
||||
descriptor(&attr, pytype.to_owned().into(), value.clone(), vm)?;
|
||||
// After successful setattro, check if _fields_ and call process_fields
|
||||
|
||||
@@ -3431,7 +3431,7 @@ mod _io {
|
||||
num_to_skip -= n_decoded;
|
||||
break;
|
||||
}
|
||||
skip_bytes -= dec_buffer.len() as isize;
|
||||
skip_bytes -= dec_buffer.as_bytes().len() as isize;
|
||||
skip_back = 1;
|
||||
} else {
|
||||
skip_bytes -= skip_back;
|
||||
|
||||
@@ -71,7 +71,7 @@ pub fn offer_suggestions(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> Opti
|
||||
if vm.is_none(&name) {
|
||||
return None;
|
||||
}
|
||||
let tb = exc.__traceback__()?;
|
||||
let tb = exc.traceback()?;
|
||||
let tb = tb.iter().last().unwrap_or(tb);
|
||||
|
||||
let varnames = tb.frame.iframe().code().to_owned().co_varnames(vm);
|
||||
|
||||
@@ -427,14 +427,16 @@ impl SlotAccessor {
|
||||
|
||||
macro_rules! inherit_main {
|
||||
($slot:ident) => {{
|
||||
let inherited = mro.iter().find_map(|cls| cls.slots.$slot.load());
|
||||
let inherited = mro.iter().find_map(|cls| cls.payload().slots.$slot.load());
|
||||
typ.slots.$slot.store(inherited);
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! inherit_number {
|
||||
($slot:ident) => {{
|
||||
let inherited = mro.iter().find_map(|cls| cls.slots.as_number.$slot.load());
|
||||
let inherited = mro
|
||||
.iter()
|
||||
.find_map(|cls| cls.payload().slots.as_number.$slot.load());
|
||||
typ.slots.as_number.$slot.store(inherited);
|
||||
}};
|
||||
}
|
||||
@@ -443,14 +445,16 @@ impl SlotAccessor {
|
||||
($slot:ident) => {{
|
||||
let inherited = mro
|
||||
.iter()
|
||||
.find_map(|cls| cls.slots.as_sequence.$slot.load());
|
||||
.find_map(|cls| cls.payload().slots.as_sequence.$slot.load());
|
||||
typ.slots.as_sequence.$slot.store(inherited);
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! inherit_mapping {
|
||||
($slot:ident) => {{
|
||||
let inherited = mro.iter().find_map(|cls| cls.slots.as_mapping.$slot.load());
|
||||
let inherited = mro
|
||||
.iter()
|
||||
.find_map(|cls| cls.payload().slots.as_mapping.$slot.load());
|
||||
typ.slots.as_mapping.$slot.store(inherited);
|
||||
}};
|
||||
}
|
||||
@@ -469,8 +473,8 @@ impl SlotAccessor {
|
||||
// vectorcall as a constructor fast path (call=None).
|
||||
// See vectorcall_type() in type.rs for the dual-use design rationale.
|
||||
let inherited_vc = mro.iter().find_map(|cls| {
|
||||
if cls.slots.call.load().is_some() {
|
||||
cls.slots.vectorcall.load()
|
||||
if cls.payload().slots.call.load().is_some() {
|
||||
cls.payload().slots.vectorcall.load()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -543,13 +547,19 @@ impl SlotAccessor {
|
||||
|
||||
// Buffer protocol
|
||||
Self::BfGetBuffer => {
|
||||
let inherited = mro.iter().find_map(|cls| cls.slots.as_buffer.load());
|
||||
let inherited = mro
|
||||
.iter()
|
||||
.find_map(|cls| cls.payload().slots.as_buffer.load());
|
||||
typ.slots.as_buffer.store(inherited);
|
||||
}
|
||||
Self::BfReleaseBuffer => {
|
||||
let has_release = mro.iter().any(|cls| cls.slots.has_release_buffer.load());
|
||||
let has_release = mro
|
||||
.iter()
|
||||
.any(|cls| cls.payload().slots.has_release_buffer.load());
|
||||
typ.slots.has_release_buffer.store(has_release);
|
||||
let py_release = mro.iter().any(|cls| cls.slots.python_release_buffer.load());
|
||||
let py_release = mro
|
||||
.iter()
|
||||
.any(|cls| cls.payload().slots.python_release_buffer.load());
|
||||
typ.slots.python_release_buffer.store(py_release);
|
||||
}
|
||||
|
||||
@@ -623,7 +633,8 @@ impl SlotAccessor {
|
||||
&& let Some(base_val) = base.slots.init.load()
|
||||
{
|
||||
let slot_defined = base.base.deref().is_none_or(|bb| {
|
||||
bb.slots.init.load().map(|v| fn_addr(v)) != Some(fn_addr(base_val))
|
||||
bb.payload().slots.init.load().map(|v| fn_addr(v))
|
||||
!= Some(fn_addr(base_val))
|
||||
});
|
||||
if slot_defined {
|
||||
typ.slots.init.store(Some(base_val));
|
||||
|
||||
@@ -1637,7 +1637,7 @@ impl VirtualMachine {
|
||||
let unraisablehook = sys_module.get_attr("unraisablehook", self).unwrap();
|
||||
|
||||
let exc_type = e.class().to_owned();
|
||||
let exc_traceback = e.__traceback__().to_pyobject(self); // TODO: actual traceback
|
||||
let exc_traceback = e.traceback().to_pyobject(self); // TODO: actual traceback
|
||||
let exc_value = e.into();
|
||||
let args = stdlib::sys::UnraisableHookArgsData {
|
||||
exc_type,
|
||||
@@ -3439,7 +3439,7 @@ impl VirtualMachine {
|
||||
attr_name: impl AsPyStr<'a>,
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
let attr_name = attr_name.as_pystr(&self.ctx);
|
||||
let getattro = obj.class().slots.getattro.load().unwrap();
|
||||
let getattro = obj.class().slots().getattro.load().unwrap();
|
||||
let result = if fn_addr(getattro) == fn_addr(PyBaseObject::getattro as GetattroFunc) {
|
||||
obj.generic_getattr_opt(attr_name, None, self)
|
||||
} else {
|
||||
|
||||
@@ -109,7 +109,7 @@ impl VirtualMachine {
|
||||
obj: Option<&PyObject>,
|
||||
cls: Option<&PyObject>,
|
||||
) -> Option<PyResult> {
|
||||
let descr_get = descr.class().slots.descr_get.load()?;
|
||||
let descr_get = descr.class().slots().descr_get.load()?;
|
||||
Some(descr_get(descr, obj, cls, self))
|
||||
}
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ impl VirtualMachine {
|
||||
iop_slot: PyNumberBinaryOp,
|
||||
op_slot: PyNumberBinaryOp,
|
||||
) -> PyResult {
|
||||
if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) {
|
||||
if let Some(slot) = a.class().slots().as_number.left_binary_op(iop_slot) {
|
||||
let x = slot(a, b, self)?;
|
||||
if !x.is(&self.ctx.not_implemented) {
|
||||
return Ok(x);
|
||||
@@ -419,7 +419,7 @@ impl VirtualMachine {
|
||||
op_slot: PyNumberTernaryOp,
|
||||
op_str: &str,
|
||||
) -> PyResult {
|
||||
if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) {
|
||||
if let Some(slot) = a.class().slots().as_number.left_ternary_op(iop_slot) {
|
||||
let x = slot(a, b, c, self)?;
|
||||
if !x.is(&self.ctx.not_implemented) {
|
||||
return Ok(x);
|
||||
|
||||
Reference in new issue
Block a user