1use crate::{
5 AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
6 builtins::{
7 PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, PyTupleRef,
8 PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr,
9 },
10 common::{hash::PyHash, str::to_ascii},
11 convert::{ToPyObject, ToPyResult},
12 dict_inner::DictKey,
13 function::{Either, FuncArgs, PyArithmeticValue, PySetterValue},
14 object::PyPayload,
15 protocol::PyIter,
16 types::{Constructor, PyComparisonOp},
17};
18
19impl PyObjectRef {
24 #[inline(always)]
30 pub fn rich_compare(self, other: Self, op_id: PyComparisonOp, vm: &VirtualMachine) -> PyResult {
31 self._cmp(&other, op_id, vm).map(|res| res.to_pyobject(vm))
32 }
33
34 pub fn bytes(self, vm: &VirtualMachine) -> PyResult {
35 let bytes_type = vm.ctx.types.bytes_type;
36 match self.downcast_exact::<PyInt>(vm) {
37 Ok(int) => Err(vm.new_downcast_type_error(bytes_type, &int)),
38 Err(obj) => {
39 let args = FuncArgs::from(vec![obj]);
40 <PyBytes as Constructor>::slot_new(bytes_type.to_owned(), args, vm)
41 }
42 }
43 }
44
45 pub fn is_true(self, vm: &VirtualMachine) -> PyResult<bool> {
48 self.try_to_bool(vm)
49 }
50
51 pub fn not(self, vm: &VirtualMachine) -> PyResult<bool> {
52 self.is_true(vm).map(|x| !x)
53 }
54
55 pub fn length_hint(self, defaultvalue: usize, vm: &VirtualMachine) -> PyResult<usize> {
56 Ok(vm.length_hint_opt(self)?.unwrap_or(defaultvalue))
57 }
58
59 pub fn dir(self, vm: &VirtualMachine) -> PyResult<PyList> {
61 let attributes = self.class().get_attributes();
62
63 let dict = PyDict::from_attributes(attributes, vm)?.into_ref(&vm.ctx);
64
65 if let Some(object_dict) = self.dict() {
66 vm.call_method(
67 dict.as_object(),
68 identifier!(vm, update).as_str(),
69 (object_dict,),
70 )?;
71 }
72
73 let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect();
74
75 Ok(PyList::from(attributes))
76 }
77}
78
79impl PyObject {
80 pub fn get_iter(&self, vm: &VirtualMachine) -> PyResult<PyIter> {
84 PyIter::try_from_object(vm, self.to_owned())
86 }
87
88 pub fn get_aiter(&self, vm: &VirtualMachine) -> PyResult {
90 use crate::builtins::PyCoroutine;
91
92 let aiter_method = self.class().get_attr(identifier!(vm, __aiter__));
94 let Some(_aiter_method) = aiter_method else {
95 return Err(vm.new_type_error(format!(
96 "'{}' object is not an async iterable",
97 self.class().name()
98 )));
99 };
100
101 let iterator = vm.call_special_method(self, identifier!(vm, __aiter__), ())?;
103
104 if iterator.downcast_ref::<PyCoroutine>().is_some() {
106 return Err(vm.new_type_error(
107 "'async_iterator' object cannot be interpreted as an async iterable; \
108 perhaps you forgot to call aiter()?",
109 ));
110 }
111
112 if !iterator.class().has_attr(identifier!(vm, __anext__)) {
114 return Err(vm.new_type_error(format!(
115 "'{}' object is not an async iterator",
116 iterator.class().name()
117 )));
118 }
119
120 Ok(iterator)
121 }
122
123 pub fn has_attr<'a>(&self, attr_name: impl AsPyStr<'a>, vm: &VirtualMachine) -> PyResult<bool> {
124 self.get_attr(attr_name, vm).map(|o| !vm.is_none(&o))
125 }
126
127 pub fn get_attr<'a>(&self, attr_name: impl AsPyStr<'a>, vm: &VirtualMachine) -> PyResult {
130 let attr_name = attr_name.as_pystr(&vm.ctx);
131 self.get_attr_inner(attr_name, vm)
132 }
133
134 #[cfg_attr(feature = "flame-it", flame("PyObjectRef"))]
136 #[inline]
137 pub(crate) fn get_attr_inner(&self, attr_name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
138 vm_trace!("object.__getattribute__: {:?} {:?}", self, attr_name);
139 let getattro = self.class().slots.getattro.load().unwrap();
140 getattro(self, attr_name, vm).inspect_err(|exc| {
141 vm.set_attribute_error_context(exc, self.to_owned(), attr_name.to_owned());
142 })
143 }
144
145 pub fn call_set_attr(
146 &self,
147 vm: &VirtualMachine,
148 attr_name: &Py<PyStr>,
149 attr_value: PySetterValue,
150 ) -> PyResult<()> {
151 let setattro = {
152 let cls = self.class();
153 cls.slots.setattro.load().ok_or_else(|| {
154 let has_getattr = cls.slots.getattro.load().is_some();
155 vm.new_type_error(format!(
156 "'{}' object has {} attributes ({} {})",
157 cls.name(),
158 if has_getattr { "only read-only" } else { "no" },
159 if attr_value.is_assign() {
160 "assign to"
161 } else {
162 "del"
163 },
164 attr_name
165 ))
166 })?
167 };
168 setattro(self, attr_name, attr_value, vm)
169 }
170
171 pub fn set_attr<'a>(
172 &self,
173 attr_name: impl AsPyStr<'a>,
174 attr_value: impl Into<PyObjectRef>,
175 vm: &VirtualMachine,
176 ) -> PyResult<()> {
177 let attr_name = attr_name.as_pystr(&vm.ctx);
178 let attr_value = attr_value.into();
179 self.call_set_attr(vm, attr_name, PySetterValue::Assign(attr_value))
180 }
181
182 #[cfg_attr(feature = "flame-it", flame)]
184 pub fn generic_setattr(
185 &self,
186 attr_name: &Py<PyStr>,
187 value: PySetterValue,
188 vm: &VirtualMachine,
189 ) -> PyResult<()> {
190 vm_trace!("object.__setattr__({:?}, {}, {:?})", self, attr_name, value);
191 if let Some(attr) = vm
192 .ctx
193 .interned_str(attr_name)
194 .and_then(|attr_name| self.get_class_attr(attr_name))
195 {
196 let descr_set = attr.class().slots.descr_set.load();
197 if let Some(descriptor) = descr_set {
198 return descriptor(&attr, self.to_owned(), value, vm);
199 }
200 }
201
202 if let Some(dict) = self.dict() {
203 if let PySetterValue::Assign(value) = value {
204 dict.set_item(attr_name, value, vm)?;
205 } else {
206 dict.del_item(attr_name, vm).map_err(|e| {
207 if e.fast_isinstance(vm.ctx.exceptions.key_error) {
208 vm.new_no_attribute_error(self.to_owned(), attr_name.to_owned())
209 } else {
210 e
211 }
212 })?;
213 }
214 Ok(())
215 } else {
216 Err(vm.new_no_attribute_error(self.to_owned(), attr_name.to_owned()))
217 }
218 }
219
220 pub fn generic_getattr(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
221 self.generic_getattr_opt(name, None, vm)?
222 .ok_or_else(|| vm.new_no_attribute_error(self.to_owned(), name.to_owned()))
223 }
224
225 pub fn generic_getattr_opt(
227 &self,
228 name_str: &Py<PyStr>,
229 dict: Option<PyDictRef>,
230 vm: &VirtualMachine,
231 ) -> PyResult<Option<PyObjectRef>> {
232 let name = name_str.as_wtf8();
233 let obj_cls = self.class();
234 let cls_attr_name = vm.ctx.interned_str(name_str);
235 let cls_attr = match cls_attr_name.and_then(|name| obj_cls.get_attr(name)) {
236 Some(descr) => {
237 let descr_cls = descr.class();
238 let descr_get = descr_cls.slots.descr_get.load();
239 if let Some(descr_get) = descr_get
240 && descr_cls.slots.descr_set.load().is_some()
241 {
242 let cls = obj_cls.to_owned().into();
243 return descr_get(descr, Some(self.to_owned()), Some(cls), vm).map(Some);
244 }
245 Some((descr, descr_get))
246 }
247 None => None,
248 };
249
250 let dict = dict.or_else(|| self.dict());
251
252 let attr = if let Some(dict) = dict {
253 dict.get_item_opt(name, vm)?
254 } else {
255 None
256 };
257
258 if let Some(obj_attr) = attr {
259 Ok(Some(obj_attr))
260 } else if let Some((attr, descr_get)) = cls_attr {
261 match descr_get {
262 Some(descr_get) => {
263 let cls = obj_cls.to_owned().into();
264 descr_get(attr, Some(self.to_owned()), Some(cls), vm).map(Some)
265 }
266 None => Ok(Some(attr)),
267 }
268 } else {
269 Ok(None)
270 }
271 }
272
273 pub fn del_attr<'a>(&self, attr_name: impl AsPyStr<'a>, vm: &VirtualMachine) -> PyResult<()> {
274 let attr_name = attr_name.as_pystr(&vm.ctx);
275 self.call_set_attr(vm, attr_name, PySetterValue::Delete)
276 }
277
278 #[inline] fn _cmp(
283 &self,
284 other: &Self,
285 op: PyComparisonOp,
286 vm: &VirtualMachine,
287 ) -> PyResult<Either<PyObjectRef, bool>> {
288 vm.with_recursion("in comparison", || self._cmp_inner(other, op, vm))
291 }
292
293 fn _cmp_inner(
294 &self,
295 other: &Self,
296 op: PyComparisonOp,
297 vm: &VirtualMachine,
298 ) -> PyResult<Either<PyObjectRef, bool>> {
299 let swapped = op.swapped();
300 let call_cmp = |obj: &Self, other: &Self, op| {
301 let Some(cmp) = obj.class().slots.richcompare.load() else {
302 return Ok(PyArithmeticValue::NotImplemented);
303 };
304 let r = match cmp(obj, other, op, vm)? {
305 Either::A(obj) => PyArithmeticValue::from_object(vm, obj).map(Either::A),
306 Either::B(arithmetic) => arithmetic.map(Either::B),
307 };
308 Ok(r)
309 };
310
311 let mut checked_reverse_op = false;
312 let is_strict_subclass = {
313 let self_class = self.class();
314 let other_class = other.class();
315 !self_class.is(other_class) && other_class.fast_issubclass(self_class)
316 };
317 if is_strict_subclass {
318 let res = call_cmp(other, self, swapped)?;
319 checked_reverse_op = true;
320 if let PyArithmeticValue::Implemented(x) = res {
321 return Ok(x);
322 }
323 }
324 if let PyArithmeticValue::Implemented(x) = call_cmp(self, other, op)? {
325 return Ok(x);
326 }
327 if !checked_reverse_op {
328 let res = call_cmp(other, self, swapped)?;
329 if let PyArithmeticValue::Implemented(x) = res {
330 return Ok(x);
331 }
332 }
333 match op {
334 PyComparisonOp::Eq => Ok(Either::B(self.is(&other))),
335 PyComparisonOp::Ne => Ok(Either::B(!self.is(&other))),
336 _ => Err(vm.new_type_error(format!(
337 "'{}' not supported between instances of '{}' and '{}'",
338 op.operator_token(),
339 self.class().name(),
340 other.class().name()
341 ))),
342 }
343 }
344 #[inline(always)]
345 pub fn rich_compare_bool(
346 &self,
347 other: &Self,
348 op_id: PyComparisonOp,
349 vm: &VirtualMachine,
350 ) -> PyResult<bool> {
351 match self._cmp(other, op_id, vm)? {
352 Either::A(obj) => obj.try_to_bool(vm),
353 Either::B(other) => Ok(other),
354 }
355 }
356
357 pub fn repr_utf8(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyUtf8Str>> {
358 self.repr(vm)?.try_into_utf8(vm)
359 }
360
361 pub fn repr(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
362 vm.with_recursion("while getting the repr of an object", || {
363 self.class().slots.repr.load().map_or_else(
364 || {
365 Err(vm.new_runtime_error(format!(
366 "BUG: object of type '{}' has no __repr__ method. This is a bug in RustPython.",
367 self.class().name()
368 )))
369 },
370 |repr| repr(self, vm),
371 )
372 })
373 }
374
375 pub fn ascii(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
376 let repr = self.repr(vm)?;
377 if repr.as_wtf8().is_ascii() {
378 Ok(repr)
379 } else {
380 Ok(vm.ctx.new_str(to_ascii(repr.as_wtf8())))
381 }
382 }
383
384 pub fn str_utf8(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyUtf8Str>> {
385 self.str(vm)?.try_into_utf8(vm)
386 }
387 pub fn str(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
388 let obj = match self.to_owned().downcast_exact::<PyStr>(vm) {
389 Ok(s) => return Ok(s.into_pyref()),
390 Err(obj) => obj,
391 };
392 let obj = match obj.downcast_exact::<PyInt>(vm) {
394 Ok(int) => {
395 return Ok(vm.ctx.new_str(int.to_str_radix_10()));
396 }
397 Err(obj) => obj,
398 };
399 let str_method = match vm.get_special_method(&obj, identifier!(vm, __str__))? {
401 Some(str_method) => str_method,
402 None => return obj.repr(vm),
403 };
404 let s = str_method.invoke((), vm)?;
405 s.downcast::<PyStr>().map_err(|obj| {
406 vm.new_type_error(format!(
407 "__str__ returned non-string (type {})",
408 obj.class().name()
409 ))
410 })
411 }
412
413 fn check_class<F>(&self, vm: &VirtualMachine, msg: F) -> PyResult<()>
416 where
417 F: Fn() -> String,
418 {
419 let cls = self;
420 match cls.abstract_get_bases(vm)? {
421 Some(_bases) => Ok(()), None => {
423 Err(vm.new_type_error(msg()))
425 }
426 }
427 }
428
429 fn abstract_get_bases(&self, vm: &VirtualMachine) -> PyResult<Option<PyTupleRef>> {
439 match vm.get_attribute_opt(self.to_owned(), identifier!(vm, __bases__))? {
440 Some(bases) => {
441 match PyTupleRef::try_from_object(vm, bases) {
443 Ok(tuple) => Ok(Some(tuple)),
444 Err(_) => Ok(None), }
446 }
447 None => Ok(None), }
449 }
450
451 fn abstract_issubclass(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
452 let mut bases: PyTupleRef;
454 let mut derived = self;
455
456 let bases = loop {
458 if derived.is(cls) {
459 return Ok(true);
460 }
461
462 let Some(derived_bases) = derived.abstract_get_bases(vm)? else {
463 return Ok(false);
464 };
465
466 let n = derived_bases.len();
467 match n {
468 0 => return Ok(false),
469 1 => {
470 bases = derived_bases;
473 derived = &bases.as_slice()[0];
474 continue;
475 }
476 _ => {
477 break derived_bases;
479 }
480 }
481 };
482
483 let n = bases.len();
484 debug_assert!(n >= 2);
486
487 for i in 0..n {
488 let result = vm.with_recursion("in __issubclass__", || {
489 bases.as_slice()[i].abstract_issubclass(cls, vm)
490 })?;
491 if result {
492 return Ok(true);
493 }
494 }
495
496 Ok(false)
497 }
498
499 fn recursive_issubclass(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
500 if let Some(cls) = PyType::check(cls)
502 && let Some(derived) = PyType::check(self)
503 {
504 return Ok(derived.is_subtype(cls));
506 }
507 self.check_class(vm, || {
509 format!("issubclass() arg 1 must be a class, not {}", self.class())
510 })?;
511
512 if !cls.class().is(vm.ctx.types.union_type) {
514 cls.check_class(vm, || {
515 format!(
516 "issubclass() arg 2 must be a class, a tuple of classes, or a union, not {}",
517 cls.class()
518 )
519 })?;
520 }
521
522 self.abstract_issubclass(cls, vm)
523 }
524
525 pub fn real_is_subclass(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
528 self.recursive_issubclass(cls, vm)
529 }
530
531 pub fn is_subclass(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
535 let derived = self;
536 if cls.class().is(vm.ctx.types.type_type) {
538 if derived.is(cls) {
539 return Ok(true);
540 }
541 return derived.recursive_issubclass(cls, vm);
542 }
543
544 let cls = if cls.class().is(vm.ctx.types.union_type) {
546 let union = cls
549 .downcast_ref::<crate::builtins::PyUnion>()
550 .expect("union is already checked");
551 union.args().as_object()
552 } else {
553 cls
554 };
555
556 if let Some(tuple) = cls.downcast_ref::<PyTuple>() {
558 for item in tuple {
559 if vm.with_recursion("in __subclasscheck__", || derived.is_subclass(item, vm))? {
560 return Ok(true);
561 }
562 }
563 return Ok(false);
564 }
565
566 if let Some(checker) = cls.lookup_special(identifier!(vm, __subclasscheck__), vm) {
568 let res = vm.with_recursion("in __subclasscheck__", || {
569 checker.call((derived.to_owned(),), vm)
570 })?;
571 return res.try_to_bool(vm);
572 }
573
574 derived.recursive_issubclass(cls, vm)
575 }
576
577 pub(crate) fn real_is_instance(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
579 self.object_isinstance(cls, vm)
580 }
581
582 fn object_isinstance(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
585 if let Ok(cls) = cls.try_to_ref::<PyType>(vm) {
586 let mut retval = self.class().is_subtype(cls);
588 if !retval
589 && let Some(i_cls) =
590 vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))?
591 && let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls)
592 && !i_cls_type.is(self.class())
593 {
594 retval = i_cls_type.is_subtype(cls);
595 }
596 Ok(retval)
597 } else {
598 cls.check_class(vm, || {
600 format!(
601 "isinstance() arg 2 must be a type, a tuple of types, or a union, not {}",
602 cls.class()
603 )
604 })?;
605
606 if let Some(i_cls) =
607 vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))?
608 {
609 i_cls.abstract_issubclass(cls, vm)
610 } else {
611 Ok(false)
612 }
613 }
614 }
615
616 pub fn is_instance(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
619 self.object_recursive_isinstance(cls, vm)
620 }
621
622 fn object_recursive_isinstance(&self, cls: &Self, vm: &VirtualMachine) -> PyResult<bool> {
624 if self.class().is(cls) {
627 return Ok(true);
628 }
629
630 if cls.class().is(vm.ctx.types.type_type) {
632 return self.object_isinstance(cls, vm);
635 }
636
637 let cls = if cls.class().is(vm.ctx.types.union_type) {
639 let union = cls
641 .try_to_ref::<crate::builtins::PyUnion>(vm)
642 .expect("checked by is");
643 union.args().as_object()
644 } else {
645 cls
646 };
647
648 if let Some(tuple) = cls.downcast_ref::<PyTuple>() {
650 for item in tuple {
651 if vm.with_recursion("in __instancecheck__", || {
652 self.object_recursive_isinstance(item, vm)
653 })? {
654 return Ok(true);
655 }
656 }
657 return Ok(false);
658 }
659
660 if let Some(checker) = cls.lookup_special(identifier!(vm, __instancecheck__), vm) {
662 let res = vm.with_recursion("in __instancecheck__", || {
663 checker.call((self.to_owned(),), vm)
664 })?;
665 return res.try_to_bool(vm);
666 }
667
668 self.object_isinstance(cls, vm)
670 }
671
672 pub fn hash(&self, vm: &VirtualMachine) -> PyResult<PyHash> {
673 if let Some(hash) = self.class().slots.hash.load() {
674 return hash(self, vm);
675 }
676
677 Err(vm.new_exception_msg(
678 vm.ctx.exceptions.type_error.to_owned(),
679 format!("unhashable type: '{}'", self.class().name()).into(),
680 ))
681 }
682
683 pub fn obj_type(&self) -> PyObjectRef {
686 self.class().to_owned().into()
687 }
688
689 pub fn type_check(&self, typ: &Py<PyType>) -> bool {
691 self.fast_isinstance(typ)
692 }
693
694 pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> {
695 self.sequence_unchecked()
696 .length_opt(vm)
697 .or_else(|| self.mapping_unchecked().length_opt(vm))
698 }
699
700 pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
701 self.length_opt(vm).ok_or_else(|| {
702 vm.new_type_error(format!(
703 "object of type '{}' has no len()",
704 self.class().name()
705 ))
706 })?
707 }
708
709 pub fn get_item<K: DictKey + ?Sized>(&self, needle: &K, vm: &VirtualMachine) -> PyResult {
710 if let Some(dict) = self.downcast_ref_if_exact::<PyDict>(vm) {
711 return dict.get_item(needle, vm);
712 }
713
714 let needle = needle.to_pyobject(vm);
715
716 if let Ok(mapping) = self.try_mapping(vm) {
717 mapping.subscript(&needle, vm)
718 } else if let Ok(seq) = self.try_sequence(vm) {
719 let i = needle.key_as_isize(vm)?;
720 seq.get_item(i, vm)
721 } else {
722 if self.class().fast_issubclass(vm.ctx.types.type_type) {
723 if self.is(vm.ctx.types.type_type) {
724 return PyGenericAlias::from_args(self.class().to_owned(), needle, vm)
725 .to_pyresult(vm);
726 }
727
728 if let Some(class_getitem) =
729 vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class_getitem__))?
730 && !vm.is_none(&class_getitem)
731 {
732 return class_getitem.call((needle,), vm);
733 }
734 return Err(vm.new_type_error(format!(
735 "type '{}' is not subscriptable",
736 self.downcast_ref::<PyType>().unwrap().name()
737 )));
738 }
739 Err(vm.new_type_error(format!("'{}' object is not subscriptable", self.class())))
740 }
741 }
742
743 pub fn set_item<K: DictKey + ?Sized>(
744 &self,
745 needle: &K,
746 value: PyObjectRef,
747 vm: &VirtualMachine,
748 ) -> PyResult<()> {
749 if let Some(dict) = self.downcast_ref_if_exact::<PyDict>(vm) {
750 return dict.set_item(needle, value, vm);
751 }
752
753 let mapping = self.mapping_unchecked();
754 if let Some(f) = mapping.slots().ass_subscript.load() {
755 let needle = needle.to_pyobject(vm);
756 return f(mapping, &needle, Some(value), vm);
757 }
758
759 let seq = self.sequence_unchecked();
760 if let Some(f) = seq.slots().ass_item.load() {
761 let i = needle.key_as_isize(vm)?;
762 return f(seq, i, Some(value), vm);
763 }
764
765 Err(vm.new_type_error(format!(
766 "'{}' does not support item assignment",
767 self.class()
768 )))
769 }
770
771 pub fn del_item<K: DictKey + ?Sized>(&self, needle: &K, vm: &VirtualMachine) -> PyResult<()> {
772 if let Some(dict) = self.downcast_ref_if_exact::<PyDict>(vm) {
773 return dict.del_item(needle, vm);
774 }
775
776 let mapping = self.mapping_unchecked();
777 if let Some(f) = mapping.slots().ass_subscript.load() {
778 let needle = needle.to_pyobject(vm);
779 return f(mapping, &needle, None, vm);
780 }
781 let seq = self.sequence_unchecked();
782 if let Some(f) = seq.slots().ass_item.load() {
783 let i = needle.key_as_isize(vm)?;
784 return f(seq, i, None, vm);
785 }
786
787 Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class())))
788 }
789
790 pub fn lookup_special(&self, attr: &Py<PyStr>, vm: &VirtualMachine) -> Option<PyObjectRef> {
794 let obj_cls = self.class();
795
796 let res = obj_cls.lookup_ref(attr, vm)?;
798
799 let descr_get = res.class().slots.descr_get.load();
801 if let Some(descr_get) = descr_get {
802 let obj_cls = obj_cls.to_owned().into();
803 descr_get(res, Some(self.to_owned()), Some(obj_cls), vm).ok()
805 } else {
806 Some(res)
807 }
808 }
809}