1use super::{
2 IterStatus, PositionIterInternal, PyBaseExceptionRef, PyGenericAlias, PyMappingProxy, PySet,
3 PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, set::PySetInner,
4};
5use crate::common::lock::LazyLock;
6use crate::object::{Traverse, TraverseFn};
7use crate::{
8 AsObject, Context, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
9 TryFromObject, atomic_func,
10 builtins::{
11 PyTuple,
12 iter::{builtins_iter, builtins_reversed},
13 type_::PyAttributes,
14 },
15 class::{PyClassDef, PyClassImpl},
16 common::ascii,
17 dict_inner::{self, DictKey},
18 function::{
19 ArgIterable, FuncArgs, KwArgs, OptionalArg, PyArithmeticValue::*, PyComparisonValue,
20 },
21 iter::PyExactSizeIterator,
22 protocol::{PyIterIter, PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
23 recursion::ReprGuard,
24 types::{
25 AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, DefaultConstructor,
26 Initializer, IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
27 },
28 vm::VirtualMachine,
29};
30use alloc::fmt;
31use core::cell::Cell;
32use core::ptr::NonNull;
33use rustpython_common::lock::PyMutex;
34use rustpython_common::wtf8::Wtf8Buf;
35
36pub type DictContentType = dict_inner::Dict;
37
38#[pyclass(module = false, name = "dict", unhashable = true, traverse = "manual")]
39#[derive(Default)]
40pub struct PyDict {
41 entries: DictContentType,
42}
43pub type PyDictRef = PyRef<PyDict>;
44
45unsafe impl Traverse for PyDict {
47 fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
48 self.entries.traverse(traverse_fn);
49 }
50
51 fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
52 for (key, value) in self.entries.drain_entries() {
54 out.push(key);
55 out.push(value);
56 }
57 }
58}
59
60impl fmt::Debug for PyDict {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.write_str("dict")
64 }
65}
66
67thread_local! {
68 static DICT_FREELIST: Cell<crate::object::FreeList<PyDict>> = const { Cell::new(crate::object::FreeList::new()) };
69}
70
71impl PyPayload for PyDict {
72 const MAX_FREELIST: usize = 80;
73 const HAS_FREELIST: bool = true;
74
75 #[inline]
76 fn class(ctx: &Context) -> &'static Py<PyType> {
77 ctx.types.dict_type
78 }
79
80 #[inline]
81 unsafe fn freelist_push(obj: *mut PyObject) -> bool {
82 DICT_FREELIST
83 .try_with(|fl| {
84 let mut list = fl.take();
85 let stored = if list.len() < Self::MAX_FREELIST {
86 list.push(obj);
87 true
88 } else {
89 false
90 };
91 fl.set(list);
92 stored
93 })
94 .unwrap_or(false)
95 }
96
97 #[inline]
98 unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
99 DICT_FREELIST
100 .try_with(|fl| {
101 let mut list = fl.take();
102 let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
103 fl.set(list);
104 result
105 })
106 .ok()
107 .flatten()
108 }
109}
110
111impl PyDict {
112 #[deprecated(note = "use PyDict::default().into_ref() instead")]
113 pub fn new_ref(ctx: &Context) -> PyRef<Self> {
114 Self::default().into_ref(ctx)
115 }
116
117 pub(crate) const fn _as_dict_inner(&self) -> &DictContentType {
120 &self.entries
121 }
122
123 pub(crate) fn version(&self) -> u64 {
125 self.entries.version()
126 }
127
128 pub fn keys_vec(&self) -> Vec<PyObjectRef> {
131 self.entries.keys()
132 }
133
134 pub fn values_vec(&self) -> Vec<PyObjectRef> {
137 self.entries.values()
138 }
139
140 pub fn items_vec(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
143 self.entries.items()
144 }
145
146 pub(crate) fn merge_object(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
148 let casted: Result<PyRefExact<Self>, _> = other.downcast_exact(vm);
149 let other = match casted {
150 Ok(dict_other) => return self.merge_dict(dict_other.into_pyref(), vm),
151 Err(other) => other,
152 };
153 let dict = &self.entries;
154 let keys_result = other.get_attr(vm.ctx.intern_str("keys"), vm);
156 let has_keys = match keys_result {
157 Ok(keys_method) => {
158 let keys = keys_method.call((), vm)?.get_iter(vm)?;
159 while let PyIterReturn::Return(key) = keys.next(vm)? {
160 let val = other.get_item(&*key, vm)?;
161 dict.insert(vm, &*key, val)?;
162 }
163 true
164 }
165 Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => false,
166 Err(e) => return Err(e),
167 };
168 if !has_keys {
169 let iter = other.get_iter(vm)?;
170 loop {
171 fn err(vm: &VirtualMachine) -> PyBaseExceptionRef {
172 vm.new_value_error("Iterator must have exactly two elements")
173 }
174 let element = match iter.next(vm)? {
175 PyIterReturn::Return(obj) => obj,
176 PyIterReturn::StopIteration(_) => break,
177 };
178 let elem_iter = element.get_iter(vm)?;
179 let key = elem_iter.next(vm)?.into_result().map_err(|_| err(vm))?;
180 let value = elem_iter.next(vm)?.into_result().map_err(|_| err(vm))?;
181 if matches!(elem_iter.next(vm)?, PyIterReturn::Return(_)) {
182 return Err(err(vm));
183 }
184 dict.insert(vm, &*key, value)?;
185 }
186 }
187 Ok(())
188 }
189
190 fn merge_dict(&self, dict_other: PyDictRef, vm: &VirtualMachine) -> PyResult<()> {
191 let dict = &self.entries;
192 let dict_size = &dict_other.size();
193 for (key, value) in &dict_other {
194 dict.insert(vm, &*key, value)?;
195 }
196 if dict_other.entries.has_changed_size(dict_size) {
197 return Err(vm.new_runtime_error("dict mutated during update"));
198 }
199 Ok(())
200 }
201
202 pub fn is_empty(&self) -> bool {
203 self.entries.len() == 0
204 }
205
206 pub(crate) fn inner_setitem<K: DictKey + ?Sized>(
209 &self,
210 key: &K,
211 value: PyObjectRef,
212 vm: &VirtualMachine,
213 ) -> PyResult<()> {
214 self.entries.insert(vm, key, value)
215 }
216
217 pub(crate) fn inner_delitem<K: DictKey + ?Sized>(
218 &self,
219 key: &K,
220 vm: &VirtualMachine,
221 ) -> PyResult<()> {
222 self.entries.delete(vm, key)
223 }
224
225 pub fn get_or_insert(
226 &self,
227 vm: &VirtualMachine,
228 key: PyObjectRef,
229 default: impl FnOnce() -> PyObjectRef,
230 ) -> PyResult {
231 self.entries.setdefault(vm, &*key, default)
232 }
233
234 pub fn from_attributes(attrs: PyAttributes, vm: &VirtualMachine) -> PyResult<Self> {
235 let entries = DictContentType::default();
236
237 for (key, value) in attrs {
238 entries.insert(vm, key, value)?;
239 }
240
241 Ok(Self { entries })
242 }
243
244 pub fn contains_key<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> bool {
245 self.entries.contains(vm, key).unwrap()
246 }
247
248 pub fn size(&self) -> dict_inner::DictSize {
249 self.entries.size()
250 }
251}
252
253#[allow(clippy::len_without_is_empty)]
255#[pyclass(
256 with(
257 Py,
258 PyRef,
259 Constructor,
260 Initializer,
261 Comparable,
262 Iterable,
263 AsSequence,
264 AsNumber,
265 AsMapping,
266 Representable
267 ),
268 flags(BASETYPE, MAPPING, _MATCH_SELF)
269)]
270impl PyDict {
271 #[pyclassmethod]
272 fn fromkeys(
273 class: PyTypeRef,
274 iterable: ArgIterable,
275 value: OptionalArg<PyObjectRef>,
276 vm: &VirtualMachine,
277 ) -> PyResult {
278 let value = value.unwrap_or_none(vm);
279 let d = PyType::call(&class, ().into(), vm)?;
280 match d.downcast_exact::<Self>(vm) {
281 Ok(pydict) => {
282 for key in iterable.iter(vm)? {
283 pydict.__setitem__(key?, value.clone(), vm)?;
284 }
285 Ok(pydict.into_pyref().into())
286 }
287 Err(pyobj) => {
288 for key in iterable.iter(vm)? {
289 pyobj.set_item(&*key?, value.clone(), vm)?;
290 }
291 Ok(pyobj)
292 }
293 }
294 }
295
296 pub fn __len__(&self) -> usize {
297 self.entries.len()
298 }
299
300 #[pymethod]
301 fn __sizeof__(&self) -> usize {
302 core::mem::size_of::<Self>() + self.entries.sizeof()
303 }
304
305 fn __contains__(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
306 self.entries.contains(vm, &*key)
307 }
308
309 fn __delitem__(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
310 self.inner_delitem(&*key, vm)
311 }
312
313 #[pymethod]
314 pub fn clear(&self) {
315 self.entries.clear()
316 }
317
318 fn __setitem__(
319 &self,
320 key: PyObjectRef,
321 value: PyObjectRef,
322 vm: &VirtualMachine,
323 ) -> PyResult<()> {
324 self.inner_setitem(&*key, value, vm)
325 }
326
327 #[pymethod]
328 fn get(
329 &self,
330 key: PyObjectRef,
331 default: OptionalArg<PyObjectRef>,
332 vm: &VirtualMachine,
333 ) -> PyResult {
334 match self.entries.get(vm, &*key)? {
335 Some(value) => Ok(value),
336 None => Ok(default.unwrap_or_none(vm)),
337 }
338 }
339
340 #[pymethod]
341 fn setdefault(
342 &self,
343 key: PyObjectRef,
344 default: OptionalArg<PyObjectRef>,
345 vm: &VirtualMachine,
346 ) -> PyResult {
347 self.entries
348 .setdefault(vm, &*key, || default.unwrap_or_none(vm))
349 }
350
351 #[pymethod]
352 pub fn copy(&self) -> Self {
353 Self {
354 entries: self.entries.clone(),
355 }
356 }
357
358 #[pymethod]
359 fn update(
360 &self,
361 dict_obj: OptionalArg<PyObjectRef>,
362 kwargs: KwArgs,
363 vm: &VirtualMachine,
364 ) -> PyResult<()> {
365 if let OptionalArg::Present(dict_obj) = dict_obj {
366 self.merge_object(dict_obj, vm)?;
367 }
368 for (key, value) in kwargs {
369 self.entries.insert(vm, &key, value)?;
370 }
371 Ok(())
372 }
373
374 fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
375 let other_dict: Result<PyDictRef, _> = other.downcast();
376 if let Ok(other) = other_dict {
377 let self_cp = self.copy();
378 self_cp.merge_dict(other, vm)?;
379 return Ok(self_cp.into_pyobject(vm));
380 }
381 Ok(vm.ctx.not_implemented())
382 }
383
384 #[pymethod]
385 fn pop(
386 &self,
387 key: PyObjectRef,
388 default: OptionalArg<PyObjectRef>,
389 vm: &VirtualMachine,
390 ) -> PyResult {
391 match self.entries.pop(vm, &*key)? {
392 Some(value) => Ok(value),
393 None => default.ok_or_else(|| vm.new_key_error(key)),
394 }
395 }
396
397 #[pymethod]
398 fn popitem(&self, vm: &VirtualMachine) -> PyResult<(PyObjectRef, PyObjectRef)> {
399 let (key, value) = self.entries.pop_back().ok_or_else(|| {
400 let err_msg = vm
401 .ctx
402 .new_str(ascii!("popitem(): dictionary is empty"))
403 .into();
404 vm.new_key_error(err_msg)
405 })?;
406 Ok((key, value))
407 }
408
409 #[pyclassmethod]
410 fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
411 PyGenericAlias::from_args(cls, args, vm)
412 }
413}
414
415#[pyclass]
416impl Py<PyDict> {
417 fn inner_cmp(
418 &self,
419 other: &Self,
420 op: PyComparisonOp,
421 item: bool,
422 vm: &VirtualMachine,
423 ) -> PyResult<PyComparisonValue> {
424 if op == PyComparisonOp::Ne {
425 return Self::inner_cmp(self, other, PyComparisonOp::Eq, item, vm)
426 .map(|x| x.map(|eq| !eq));
427 }
428 if !op.eval_ord(self.__len__().cmp(&other.__len__())) {
429 return Ok(Implemented(false));
430 }
431 let (superset, subset) = if self.__len__() < other.__len__() {
432 (other, self)
433 } else {
434 (self, other)
435 };
436 for (k, v1) in subset {
437 match superset.get_item_opt(&*k, vm)? {
438 Some(v2) => {
439 if v1.is(&v2) {
440 continue;
441 }
442 if item && !vm.bool_eq(&v1, &v2)? {
443 return Ok(Implemented(false));
444 }
445 }
446 None => {
447 return Ok(Implemented(false));
448 }
449 }
450 }
451 Ok(Implemented(true))
452 }
453
454 #[cfg_attr(feature = "flame-it", flame("PyDictRef"))]
455 fn __getitem__(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
456 self.inner_getitem(&*key, vm)
457 }
458}
459
460#[pyclass]
461impl PyRef<PyDict> {
462 #[pymethod]
463 const fn keys(self) -> PyDictKeys {
464 PyDictKeys::new(self)
465 }
466
467 #[pymethod]
468 const fn values(self) -> PyDictValues {
469 PyDictValues::new(self)
470 }
471
472 #[pymethod]
473 const fn items(self) -> PyDictItems {
474 PyDictItems::new(self)
475 }
476
477 #[pymethod]
478 fn __reversed__(self) -> PyDictReverseKeyIterator {
479 PyDictReverseKeyIterator::new(self)
480 }
481
482 fn __ior__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
483 self.merge_object(other, vm)?;
484 Ok(self)
485 }
486
487 fn __ror__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
488 let other_dict: Result<Self, _> = other.downcast();
489 if let Ok(other) = other_dict {
490 let other_cp = other.copy();
491 other_cp.merge_dict(self, vm)?;
492 return Ok(other_cp.into_pyobject(vm));
493 }
494 Ok(vm.ctx.not_implemented())
495 }
496}
497
498impl DefaultConstructor for PyDict {}
499
500impl Initializer for PyDict {
501 type Args = (OptionalArg<PyObjectRef>, KwArgs);
502
503 fn init(
504 zelf: PyRef<Self>,
505 (dict_obj, kwargs): Self::Args,
506 vm: &VirtualMachine,
507 ) -> PyResult<()> {
508 zelf.update(dict_obj, kwargs, vm)
509 }
510}
511
512impl AsMapping for PyDict {
513 fn as_mapping() -> &'static PyMappingMethods {
514 static AS_MAPPING: PyMappingMethods = PyMappingMethods {
515 length: atomic_func!(|mapping, _vm| Ok(PyDict::mapping_downcast(mapping).__len__())),
516 subscript: atomic_func!(|mapping, needle, vm| {
517 PyDict::mapping_downcast(mapping).inner_getitem(needle, vm)
518 }),
519 ass_subscript: atomic_func!(|mapping, needle, value, vm| {
520 let zelf = PyDict::mapping_downcast(mapping);
521 if let Some(value) = value {
522 zelf.inner_setitem(needle, value, vm)
523 } else {
524 zelf.inner_delitem(needle, vm)
525 }
526 }),
527 };
528 &AS_MAPPING
529 }
530}
531
532impl AsSequence for PyDict {
533 fn as_sequence() -> &'static PySequenceMethods {
534 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
535 contains: atomic_func!(|seq, target, vm| PyDict::sequence_downcast(seq)
536 .entries
537 .contains(vm, target)),
538 ..PySequenceMethods::NOT_IMPLEMENTED
539 });
540 &AS_SEQUENCE
541 }
542}
543
544impl AsNumber for PyDict {
545 fn as_number() -> &'static PyNumberMethods {
546 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
547 or: Some(|a, b, vm| {
548 if let Some(a) = a.downcast_ref::<PyDict>() {
549 PyDict::__or__(a, b.to_pyobject(vm), vm)
550 } else {
551 Ok(vm.ctx.not_implemented())
552 }
553 }),
554 inplace_or: Some(|a, b, vm| {
555 if let Some(a) = a.downcast_ref::<PyDict>() {
556 a.to_owned()
557 .__ior__(b.to_pyobject(vm), vm)
558 .map(|d| d.into())
559 } else {
560 Ok(vm.ctx.not_implemented())
561 }
562 }),
563 ..PyNumberMethods::NOT_IMPLEMENTED
564 };
565 &AS_NUMBER
566 }
567}
568
569impl Comparable for PyDict {
570 fn cmp(
571 zelf: &Py<Self>,
572 other: &PyObject,
573 op: PyComparisonOp,
574 vm: &VirtualMachine,
575 ) -> PyResult<PyComparisonValue> {
576 op.eq_only(|| {
577 let other = class_or_notimplemented!(Self, other);
578 zelf.inner_cmp(other, PyComparisonOp::Eq, true, vm)
579 })
580 }
581}
582
583impl Iterable for PyDict {
584 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
585 Ok(PyDictKeyIterator::new(zelf).into_pyobject(vm))
586 }
587}
588
589impl Representable for PyDict {
590 #[inline]
591 fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
592 let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
593 let mut result = Wtf8Buf::from("{");
594 let mut first = true;
595 for (key, value) in zelf {
596 if !first {
597 result.push_str(", ");
598 }
599 first = false;
600 result.push_wtf8(key.repr(vm)?.as_wtf8());
601 result.push_str(": ");
602 result.push_wtf8(value.repr(vm)?.as_wtf8());
603 }
604 result.push_char('}');
605 vm.ctx.new_str(result)
606 } else {
607 vm.ctx.intern_str("{...}").to_owned()
608 };
609 Ok(s)
610 }
611
612 #[cold]
613 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
614 unreachable!("use repr instead")
615 }
616}
617
618impl Py<PyDict> {
619 #[inline]
620 fn exact_dict(&self, vm: &VirtualMachine) -> bool {
621 self.class().is(vm.ctx.types.dict_type)
622 }
623
624 fn missing_opt<K: DictKey + ?Sized>(
625 &self,
626 key: &K,
627 vm: &VirtualMachine,
628 ) -> PyResult<Option<PyObjectRef>> {
629 vm.get_method(self.to_owned().into(), identifier!(vm, __missing__))
630 .map(|methods| methods?.call((key.to_pyobject(vm),), vm))
631 .transpose()
632 }
633
634 #[inline]
635 fn inner_getitem<K: DictKey + ?Sized>(
636 &self,
637 key: &K,
638 vm: &VirtualMachine,
639 ) -> PyResult<PyObjectRef> {
640 if let Some(value) = self.entries.get(vm, key)? {
641 Ok(value)
642 } else if let Some(value) = self.missing_opt(key, vm)? {
643 Ok(value)
644 } else {
645 Err(vm.new_key_error(key.to_pyobject(vm)))
646 }
647 }
648
649 pub fn to_attributes(&self, vm: &VirtualMachine) -> PyAttributes {
651 let mut attrs = PyAttributes::default();
652 for (key, value) in self {
653 let key: PyRefExact<PyStr> = key.downcast_exact(vm).expect("dict has non-string keys");
654 attrs.insert(vm.ctx.intern_str(key), value);
655 }
656 attrs
657 }
658
659 pub fn get_item_opt<K: DictKey + ?Sized>(
660 &self,
661 key: &K,
662 vm: &VirtualMachine,
663 ) -> PyResult<Option<PyObjectRef>> {
664 if self.exact_dict(vm) {
665 self.entries.get(vm, key)
666 } else {
668 match self.as_object().get_item(key, vm) {
669 Ok(value) => Ok(Some(value)),
670 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
671 self.missing_opt(key, vm)
672 }
673 Err(e) => Err(e),
674 }
675 }
676 }
677
678 pub(crate) fn hint_for_key<K: DictKey + ?Sized>(
680 &self,
681 key: &K,
682 vm: &VirtualMachine,
683 ) -> PyResult<Option<u16>> {
684 if self.exact_dict(vm) {
685 self.entries.hint_for_key(vm, key)
686 } else {
687 Ok(None)
688 }
689 }
690
691 pub(crate) fn get_item_opt_hint<K: DictKey + ?Sized>(
693 &self,
694 key: &K,
695 hint: u16,
696 vm: &VirtualMachine,
697 ) -> PyResult<Option<PyObjectRef>> {
698 if self.exact_dict(vm) {
699 self.entries.get_hint(vm, key, usize::from(hint))
700 } else {
701 self.get_item_opt(key, vm)
702 }
703 }
704
705 pub fn get_item<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> PyResult {
706 if self.exact_dict(vm) {
707 self.inner_getitem(key, vm)
708 } else {
709 self.as_object().get_item(key, vm)
710 }
711 }
712
713 pub fn set_item<K: DictKey + ?Sized>(
714 &self,
715 key: &K,
716 value: PyObjectRef,
717 vm: &VirtualMachine,
718 ) -> PyResult<()> {
719 if self.exact_dict(vm) {
720 self.inner_setitem(key, value, vm)
721 } else {
722 self.as_object().set_item(key, value, vm)
723 }
724 }
725
726 pub fn del_item<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> PyResult<()> {
727 if self.exact_dict(vm) {
728 self.inner_delitem(key, vm)
729 } else {
730 self.as_object().del_item(key, vm)
731 }
732 }
733
734 pub fn pop_item<K: DictKey + ?Sized>(
735 &self,
736 key: &K,
737 vm: &VirtualMachine,
738 ) -> PyResult<Option<PyObjectRef>> {
739 if self.exact_dict(vm) {
740 self.entries.remove_if_exists(vm, key)
741 } else {
742 let value = self.as_object().get_item(key, vm)?;
743 self.as_object().del_item(key, vm)?;
744 Ok(Some(value))
745 }
746 }
747
748 pub fn get_chain<K: DictKey + ?Sized>(
749 &self,
750 other: &Self,
751 key: &K,
752 vm: &VirtualMachine,
753 ) -> PyResult<Option<PyObjectRef>> {
754 let self_exact = self.exact_dict(vm);
755 let other_exact = other.exact_dict(vm);
756 if self_exact && other_exact {
757 let self_exact = unsafe { PyExact::ref_unchecked(self) };
759 let other_exact = unsafe { PyExact::ref_unchecked(other) };
760 self_exact.get_chain_exact(other_exact, key, vm)
761 } else if let Some(value) = self.get_item_opt(key, vm)? {
762 Ok(Some(value))
763 } else {
764 other.get_item_opt(key, vm)
765 }
766 }
767}
768
769impl PyExact<PyDict> {
770 pub(crate) fn get_chain_exact<K: DictKey + ?Sized>(
773 &self,
774 other: &Self,
775 key: &K,
776 vm: &VirtualMachine,
777 ) -> PyResult<Option<PyObjectRef>> {
778 debug_assert!(self.class().is(vm.ctx.types.dict_type));
779 debug_assert!(other.class().is(vm.ctx.types.dict_type));
780 self.entries.get_chain(&other.entries, vm, key)
781 }
782}
783
784impl IntoIterator for PyDictRef {
786 type Item = (PyObjectRef, PyObjectRef);
787 type IntoIter = DictIntoIter;
788
789 fn into_iter(self) -> Self::IntoIter {
790 DictIntoIter::new(self)
791 }
792}
793
794impl<'a> IntoIterator for &'a PyDictRef {
795 type Item = (PyObjectRef, PyObjectRef);
796 type IntoIter = DictIter<'a>;
797
798 fn into_iter(self) -> Self::IntoIter {
799 DictIter::new(self)
800 }
801}
802
803impl<'a> IntoIterator for &'a Py<PyDict> {
804 type Item = (PyObjectRef, PyObjectRef);
805 type IntoIter = DictIter<'a>;
806
807 fn into_iter(self) -> Self::IntoIter {
808 DictIter::new(self)
809 }
810}
811
812impl<'a> IntoIterator for &'a PyDict {
813 type Item = (PyObjectRef, PyObjectRef);
814 type IntoIter = DictIter<'a>;
815
816 fn into_iter(self) -> Self::IntoIter {
817 DictIter::new(self)
818 }
819}
820
821pub struct DictIntoIter {
822 dict: PyDictRef,
823 position: usize,
824}
825
826impl DictIntoIter {
827 pub const fn new(dict: PyDictRef) -> Self {
828 Self { dict, position: 0 }
829 }
830}
831
832impl Iterator for DictIntoIter {
833 type Item = (PyObjectRef, PyObjectRef);
834
835 fn next(&mut self) -> Option<Self::Item> {
836 let (position, key, value) = self.dict.entries.next_entry(self.position)?;
837 self.position = position;
838 Some((key, value))
839 }
840
841 fn size_hint(&self) -> (usize, Option<usize>) {
842 let l = self.len();
843 (l, Some(l))
844 }
845}
846impl ExactSizeIterator for DictIntoIter {
847 fn len(&self) -> usize {
848 self.dict.entries.len_from_entry_index(self.position)
849 }
850}
851
852pub struct DictIter<'a> {
853 dict: &'a PyDict,
854 position: usize,
855}
856
857impl<'a> DictIter<'a> {
858 pub const fn new(dict: &'a PyDict) -> Self {
859 DictIter { dict, position: 0 }
860 }
861}
862
863impl Iterator for DictIter<'_> {
864 type Item = (PyObjectRef, PyObjectRef);
865
866 fn next(&mut self) -> Option<Self::Item> {
867 let (position, key, value) = self.dict.entries.next_entry(self.position)?;
868 self.position = position;
869 Some((key, value))
870 }
871
872 fn size_hint(&self) -> (usize, Option<usize>) {
873 let l = self.len();
874 (l, Some(l))
875 }
876}
877impl ExactSizeIterator for DictIter<'_> {
878 fn len(&self) -> usize {
879 self.dict.entries.len_from_entry_index(self.position)
880 }
881}
882
883#[pyclass]
884trait DictView: PyPayload + PyClassDef + Iterable + Representable {
885 type ReverseIter: PyPayload + core::fmt::Debug;
886
887 fn dict(&self) -> &Py<PyDict>;
888 fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef;
889
890 fn __len__(&self) -> usize {
891 self.dict().__len__()
892 }
893
894 #[pymethod]
895 fn __reversed__(&self) -> Self::ReverseIter;
896}
897
898macro_rules! dict_view {
899 ( $name: ident, $iter_name: ident, $reverse_iter_name: ident,
900 $class: ident, $iter_class: ident, $reverse_iter_class: ident,
901 $class_name: literal, $iter_class_name: literal, $reverse_iter_class_name: literal,
902 $result_fn: expr) => {
903 #[pyclass(module = false, name = $class_name)]
904 #[derive(Debug)]
905 pub(crate) struct $name {
906 pub dict: PyDictRef,
907 }
908
909 impl $name {
910 pub const fn new(dict: PyDictRef) -> Self {
911 $name { dict }
912 }
913 }
914
915 impl DictView for $name {
916 type ReverseIter = $reverse_iter_name;
917
918 fn dict(&self) -> &Py<PyDict> {
919 &self.dict
920 }
921
922 fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef {
923 #[allow(clippy::redundant_closure_call)]
924 $result_fn(vm, key, value)
925 }
926
927 fn __reversed__(&self) -> Self::ReverseIter {
928 $reverse_iter_name::new(self.dict.clone())
929 }
930 }
931
932 impl Iterable for $name {
933 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
934 Ok($iter_name::new(zelf.dict.clone()).into_pyobject(vm))
935 }
936 }
937
938 impl PyPayload for $name {
939 fn class(ctx: &Context) -> &'static Py<PyType> {
940 ctx.types.$class
941 }
942 }
943
944 impl Representable for $name {
945 #[inline]
946 fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
947 let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
948 let mut result = Wtf8Buf::from(format!("{}([", Self::NAME));
949 let mut first = true;
950 for (key, value) in zelf.dict().clone() {
951 if !first {
952 result.push_str(", ");
953 }
954 first = false;
955 result.push_wtf8(Self::item(vm, key, value).repr(vm)?.as_wtf8());
956 }
957 result.push_str("])");
958 vm.ctx.new_str(result)
959 } else {
960 vm.ctx.intern_str("{...}").to_owned()
961 };
962 Ok(s)
963 }
964
965 #[cold]
966 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
967 unreachable!("use repr instead")
968 }
969 }
970
971 #[pyclass(module = false, name = $iter_class_name)]
972 #[derive(Debug)]
973 pub(crate) struct $iter_name {
974 pub size: dict_inner::DictSize,
975 pub internal: PyMutex<PositionIterInternal<PyDictRef>>,
976 }
977
978 impl PyPayload for $iter_name {
979 #[inline]
980 fn class(ctx: &Context) -> &'static Py<PyType> {
981 ctx.types.$iter_class
982 }
983 }
984
985 #[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
986 impl $iter_name {
987 fn new(dict: PyDictRef) -> Self {
988 $iter_name {
989 size: dict.size(),
990 internal: PyMutex::new(PositionIterInternal::new(dict, 0)),
991 }
992 }
993
994 #[pymethod]
995 fn __length_hint__(&self) -> usize {
996 self.internal.lock().length_hint(|_| self.size.entries_size)
997 }
998
999 #[allow(clippy::redundant_closure_call)]
1000 #[pymethod]
1001 fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
1002 let iter = builtins_iter(vm);
1003 let internal = self.internal.lock();
1004 let entries = match &internal.status {
1005 IterStatus::Active(dict) => dict
1006 .into_iter()
1007 .map(|(key, value)| ($result_fn)(vm, key, value))
1008 .collect::<Vec<_>>(),
1009 IterStatus::Exhausted => vec![],
1010 };
1011 vm.new_tuple((iter, (vm.ctx.new_list(entries),)))
1012 }
1013 }
1014
1015 impl SelfIter for $iter_name {}
1016 impl IterNext for $iter_name {
1017 #[allow(clippy::redundant_closure_call)]
1018 fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1019 let mut internal = zelf.internal.lock();
1020 let next = if let IterStatus::Active(dict) = &internal.status {
1021 if dict.entries.has_changed_size(&zelf.size) {
1022 internal.status = IterStatus::Exhausted;
1023 return Err(
1024 vm.new_runtime_error("dictionary changed size during iteration")
1025 );
1026 }
1027 match dict.entries.next_entry(internal.position) {
1028 Some((position, key, value)) => {
1029 internal.position = position;
1030 PyIterReturn::Return(($result_fn)(vm, key, value))
1031 }
1032 None => {
1033 internal.status = IterStatus::Exhausted;
1034 PyIterReturn::StopIteration(None)
1035 }
1036 }
1037 } else {
1038 PyIterReturn::StopIteration(None)
1039 };
1040 Ok(next)
1041 }
1042 }
1043
1044 #[pyclass(module = false, name = $reverse_iter_class_name)]
1045 #[derive(Debug)]
1046 pub(crate) struct $reverse_iter_name {
1047 pub size: dict_inner::DictSize,
1048 internal: PyMutex<PositionIterInternal<PyDictRef>>,
1049 }
1050
1051 impl PyPayload for $reverse_iter_name {
1052 #[inline]
1053 fn class(ctx: &Context) -> &'static Py<PyType> {
1054 ctx.types.$reverse_iter_class
1055 }
1056 }
1057
1058 #[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1059 impl $reverse_iter_name {
1060 fn new(dict: PyDictRef) -> Self {
1061 let size = dict.size();
1062 let position = size.entries_size.saturating_sub(1);
1063 $reverse_iter_name {
1064 size,
1065 internal: PyMutex::new(PositionIterInternal::new(dict, position)),
1066 }
1067 }
1068
1069 #[allow(clippy::redundant_closure_call)]
1070 #[pymethod]
1071 fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
1072 let iter = builtins_reversed(vm);
1073 let internal = self.internal.lock();
1074 let entries = match &internal.status {
1076 IterStatus::Active(dict) => dict
1077 .into_iter()
1078 .map(|(key, value)| ($result_fn)(vm, key, value))
1079 .collect::<Vec<_>>(),
1080 IterStatus::Exhausted => vec![],
1081 };
1082 vm.new_tuple((iter, (vm.ctx.new_list(entries),)))
1083 }
1084
1085 #[pymethod]
1086 fn __length_hint__(&self) -> usize {
1087 self.internal
1088 .lock()
1089 .rev_length_hint(|_| self.size.entries_size)
1090 }
1091 }
1092 impl SelfIter for $reverse_iter_name {}
1093 impl IterNext for $reverse_iter_name {
1094 #[allow(clippy::redundant_closure_call)]
1095 fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1096 let mut internal = zelf.internal.lock();
1097 let next = if let IterStatus::Active(dict) = &internal.status {
1098 if dict.entries.has_changed_size(&zelf.size) {
1099 internal.status = IterStatus::Exhausted;
1100 return Err(
1101 vm.new_runtime_error("dictionary changed size during iteration")
1102 );
1103 }
1104 match dict.entries.prev_entry(internal.position) {
1105 Some((position, key, value)) => {
1106 if internal.position == position {
1107 internal.status = IterStatus::Exhausted;
1108 } else {
1109 internal.position = position;
1110 }
1111 PyIterReturn::Return(($result_fn)(vm, key, value))
1112 }
1113 None => {
1114 internal.status = IterStatus::Exhausted;
1115 PyIterReturn::StopIteration(None)
1116 }
1117 }
1118 } else {
1119 PyIterReturn::StopIteration(None)
1120 };
1121 Ok(next)
1122 }
1123 }
1124 };
1125}
1126
1127dict_view! {
1128 PyDictKeys,
1129 PyDictKeyIterator,
1130 PyDictReverseKeyIterator,
1131 dict_keys_type,
1132 dict_keyiterator_type,
1133 dict_reversekeyiterator_type,
1134 "dict_keys",
1135 "dict_keyiterator",
1136 "dict_reversekeyiterator",
1137 |_vm: &VirtualMachine, key: PyObjectRef, _value: PyObjectRef| key
1138}
1139
1140dict_view! {
1141 PyDictValues,
1142 PyDictValueIterator,
1143 PyDictReverseValueIterator,
1144 dict_values_type,
1145 dict_valueiterator_type,
1146 dict_reversevalueiterator_type,
1147 "dict_values",
1148 "dict_valueiterator",
1149 "dict_reversevalueiterator",
1150 |_vm: &VirtualMachine, _key: PyObjectRef, value: PyObjectRef| value
1151}
1152
1153dict_view! {
1154 PyDictItems,
1155 PyDictItemIterator,
1156 PyDictReverseItemIterator,
1157 dict_items_type,
1158 dict_itemiterator_type,
1159 dict_reverseitemiterator_type,
1160 "dict_items",
1161 "dict_itemiterator",
1162 "dict_reverseitemiterator",
1163 |vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef|
1164 vm.new_tuple((key, value)).into()
1165}
1166
1167#[pyclass]
1169trait ViewSetOps: DictView {
1170 fn to_set(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PySetInner> {
1171 let len = zelf.dict().__len__();
1172 let zelf: PyObjectRef = Self::iter(zelf, vm)?;
1173 let iter = PyIterIter::new(vm, zelf, Some(len));
1174 PySetInner::from_iter(iter, vm)
1175 }
1176
1177 fn __xor__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
1178 let zelf = Self::to_set(zelf, vm)?;
1179 let inner = zelf.symmetric_difference(other, vm)?;
1180 Ok(PySet { inner })
1181 }
1182
1183 fn __and__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
1184 let zelf = Self::to_set(zelf, vm)?;
1185 let inner = zelf.intersection(other, vm)?;
1186 Ok(PySet { inner })
1187 }
1188
1189 fn __or__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
1190 let zelf = Self::to_set(zelf, vm)?;
1191 let inner = zelf.union(other, vm)?;
1192 Ok(PySet { inner })
1193 }
1194
1195 fn __sub__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
1196 let zelf = Self::to_set(zelf, vm)?;
1197 let inner = zelf.difference(other, vm)?;
1198 Ok(PySet { inner })
1199 }
1200
1201 fn __rsub__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
1202 let left = PySetInner::from_iter(other.iter(vm)?, vm)?;
1203 let right = ArgIterable::try_from_object(vm, Self::iter(zelf, vm)?)?;
1204 let inner = left.difference(right, vm)?;
1205 Ok(PySet { inner })
1206 }
1207
1208 fn cmp(
1209 zelf: &Py<Self>,
1210 other: &PyObject,
1211 op: PyComparisonOp,
1212 vm: &VirtualMachine,
1213 ) -> PyResult<PyComparisonValue> {
1214 match_class!(match other {
1215 ref dictview @ Self => {
1216 return zelf.dict().inner_cmp(
1217 dictview.dict(),
1218 op,
1219 !zelf.class().is(vm.ctx.types.dict_keys_type),
1220 vm,
1221 );
1222 }
1223 ref _set @ PySet => {
1224 let inner = Self::to_set(zelf.to_owned(), vm)?;
1225 let zelf_set = PySet { inner }.into_pyobject(vm);
1226 return PySet::cmp(zelf_set.downcast_ref().unwrap(), other, op, vm);
1227 }
1228 ref _dictitems @ PyDictItems => {}
1229 ref _dictkeys @ PyDictKeys => {}
1230 _ => {
1231 return Ok(NotImplemented);
1232 }
1233 });
1234 let lhs: Vec<PyObjectRef> = zelf.as_object().to_owned().try_into_value(vm)?;
1235 let rhs: Vec<PyObjectRef> = other.to_owned().try_into_value(vm)?;
1236 lhs.iter()
1237 .richcompare(rhs.iter(), op, vm)
1238 .map(PyComparisonValue::Implemented)
1239 }
1240
1241 #[pymethod]
1242 fn isdisjoint(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
1243 let zelf = Self::to_set(zelf, vm)?;
1245 let result = zelf.isdisjoint(other, vm)?;
1246 Ok(result)
1247 }
1248}
1249
1250impl ViewSetOps for PyDictKeys {}
1251#[pyclass(
1252 flags(DISALLOW_INSTANTIATION),
1253 with(
1254 DictView,
1255 Comparable,
1256 Iterable,
1257 ViewSetOps,
1258 AsSequence,
1259 AsNumber,
1260 Representable
1261 )
1262)]
1263impl PyDictKeys {
1264 fn __contains__(zelf: PyObjectRef, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
1265 zelf.sequence_unchecked().contains(&key, vm)
1266 }
1267
1268 #[pygetset]
1269 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1270 PyMappingProxy::from(zelf.dict().to_owned())
1271 }
1272}
1273
1274impl Comparable for PyDictKeys {
1275 fn cmp(
1276 zelf: &Py<Self>,
1277 other: &PyObject,
1278 op: PyComparisonOp,
1279 vm: &VirtualMachine,
1280 ) -> PyResult<PyComparisonValue> {
1281 ViewSetOps::cmp(zelf, other, op, vm)
1282 }
1283}
1284
1285impl AsSequence for PyDictKeys {
1286 fn as_sequence() -> &'static PySequenceMethods {
1287 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1288 length: atomic_func!(|seq, _vm| Ok(PyDictKeys::sequence_downcast(seq).__len__())),
1289 contains: atomic_func!(|seq, target, vm| {
1290 PyDictKeys::sequence_downcast(seq)
1291 .dict
1292 .entries
1293 .contains(vm, target)
1294 }),
1295 ..PySequenceMethods::NOT_IMPLEMENTED
1296 });
1297 &AS_SEQUENCE
1298 }
1299}
1300
1301impl AsNumber for PyDictKeys {
1302 fn as_number() -> &'static PyNumberMethods {
1303 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1304 subtract: Some(set_inner_number_subtract),
1305 and: Some(set_inner_number_and),
1306 xor: Some(set_inner_number_xor),
1307 or: Some(set_inner_number_or),
1308 ..PyNumberMethods::NOT_IMPLEMENTED
1309 };
1310 &AS_NUMBER
1311 }
1312}
1313
1314impl ViewSetOps for PyDictItems {}
1315#[pyclass(
1316 flags(DISALLOW_INSTANTIATION),
1317 with(
1318 DictView,
1319 Comparable,
1320 Iterable,
1321 ViewSetOps,
1322 AsSequence,
1323 AsNumber,
1324 Representable
1325 )
1326)]
1327impl PyDictItems {
1328 fn __contains__(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
1329 zelf.sequence_unchecked().contains(&needle, vm)
1330 }
1331 #[pygetset]
1332 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1333 PyMappingProxy::from(zelf.dict().to_owned())
1334 }
1335}
1336
1337impl Comparable for PyDictItems {
1338 fn cmp(
1339 zelf: &Py<Self>,
1340 other: &PyObject,
1341 op: PyComparisonOp,
1342 vm: &VirtualMachine,
1343 ) -> PyResult<PyComparisonValue> {
1344 ViewSetOps::cmp(zelf, other, op, vm)
1345 }
1346}
1347
1348impl AsSequence for PyDictItems {
1349 fn as_sequence() -> &'static PySequenceMethods {
1350 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1351 length: atomic_func!(|seq, _vm| Ok(PyDictItems::sequence_downcast(seq).__len__())),
1352 contains: atomic_func!(|seq, target, vm| {
1353 let needle: &Py<PyTuple> = match target.downcast_ref() {
1354 Some(needle) => needle,
1355 None => return Ok(false),
1356 };
1357 if needle.len() != 2 {
1358 return Ok(false);
1359 }
1360
1361 let zelf = PyDictItems::sequence_downcast(seq);
1362 let key = &needle[0];
1363 if !zelf.dict.__contains__(key.to_owned(), vm)? {
1364 return Ok(false);
1365 }
1366 let value = &needle[1];
1367 let found = zelf.dict().__getitem__(key.to_owned(), vm)?;
1368 vm.identical_or_equal(&found, value)
1369 }),
1370 ..PySequenceMethods::NOT_IMPLEMENTED
1371 });
1372 &AS_SEQUENCE
1373 }
1374}
1375
1376impl AsNumber for PyDictItems {
1377 fn as_number() -> &'static PyNumberMethods {
1378 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1379 subtract: Some(set_inner_number_subtract),
1380 and: Some(set_inner_number_and),
1381 xor: Some(set_inner_number_xor),
1382 or: Some(set_inner_number_or),
1383 ..PyNumberMethods::NOT_IMPLEMENTED
1384 };
1385 &AS_NUMBER
1386 }
1387}
1388
1389#[pyclass(
1390 flags(DISALLOW_INSTANTIATION),
1391 with(DictView, Iterable, AsSequence, Representable)
1392)]
1393impl PyDictValues {
1394 #[pygetset]
1395 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1396 PyMappingProxy::from(zelf.dict().to_owned())
1397 }
1398}
1399
1400impl AsSequence for PyDictValues {
1401 fn as_sequence() -> &'static PySequenceMethods {
1402 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1403 length: atomic_func!(|seq, _vm| Ok(PyDictValues::sequence_downcast(seq).__len__())),
1404 ..PySequenceMethods::NOT_IMPLEMENTED
1405 });
1406 &AS_SEQUENCE
1407 }
1408}
1409
1410fn set_inner_number_op<F>(a: &PyObject, b: &PyObject, f: F, vm: &VirtualMachine) -> PyResult
1411where
1412 F: FnOnce(PySetInner, ArgIterable) -> PyResult<PySetInner>,
1413{
1414 let a = PySetInner::from_iter(
1415 ArgIterable::try_from_object(vm, a.to_owned())?.iter(vm)?,
1416 vm,
1417 )?;
1418 let b = ArgIterable::try_from_object(vm, b.to_owned())?;
1419 Ok(PySet { inner: f(a, b)? }.into_pyobject(vm))
1420}
1421
1422fn set_inner_number_subtract(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1423 set_inner_number_op(a, b, |a, b| a.difference(b, vm), vm)
1424}
1425
1426fn set_inner_number_and(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1427 set_inner_number_op(a, b, |a, b| a.intersection(b, vm), vm)
1428}
1429
1430fn set_inner_number_xor(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1431 set_inner_number_op(a, b, |a, b| a.symmetric_difference(b, vm), vm)
1432}
1433
1434fn set_inner_number_or(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1435 set_inner_number_op(a, b, |a, b| a.union(b, vm), vm)
1436}
1437
1438fn vectorcall_dict(
1439 zelf_obj: &PyObject,
1440 args: Vec<PyObjectRef>,
1441 nargs: usize,
1442 kwnames: Option<&[PyObjectRef]>,
1443 vm: &VirtualMachine,
1444) -> PyResult {
1445 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1446 let obj = PyDict::default().into_ref_with_type(vm, zelf.to_owned())?;
1447 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1448 PyDict::slot_init(obj.clone().into(), func_args, vm)?;
1449 Ok(obj.into())
1450}
1451
1452pub(crate) fn init(context: &'static Context) {
1453 PyDict::extend_class(context, context.types.dict_type);
1454 context
1455 .types
1456 .dict_type
1457 .slots
1458 .vectorcall
1459 .store(Some(vectorcall_dict));
1460 PyDictKeys::extend_class(context, context.types.dict_keys_type);
1461 PyDictKeyIterator::extend_class(context, context.types.dict_keyiterator_type);
1462 PyDictReverseKeyIterator::extend_class(context, context.types.dict_reversekeyiterator_type);
1463 PyDictValues::extend_class(context, context.types.dict_values_type);
1464 PyDictValueIterator::extend_class(context, context.types.dict_valueiterator_type);
1465 PyDictReverseValueIterator::extend_class(context, context.types.dict_reversevalueiterator_type);
1466 PyDictItems::extend_class(context, context.types.dict_items_type);
1467 PyDictItemIterator::extend_class(context, context.types.dict_itemiterator_type);
1468 PyDictReverseItemIterator::extend_class(context, context.types.dict_reverseitemiterator_type);
1469}