1use super::{
2 IterStatus, PositionIterInternal, PyBaseExceptionRef, PyFrozenSet, PyGenericAlias,
3 PyMappingProxy, PySet, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, locked_step, set,
4 set::PySetInner,
5};
6use crate::common::lock::LazyLock;
7use crate::object::{Traverse, TraverseFn};
8use crate::stdlib::PyOrderedDictItems;
9use crate::{
10 AsObject, Context, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
11 TryFromObject, atomic_func,
12 builtins::{PyList, PyTuple, iter::builtins_iter, type_::PyAttributes},
13 class::{PyClassDef, PyClassImpl},
14 common::{ascii, hash::PyHash},
15 dict_inner::{self, DictKey},
16 function::{ArgIterable, FuncArgs, KwArgs, OptionalArg, PyArithmeticValue, PyComparisonValue},
17 protocol::{PyIter, PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
18 recursion::ReprGuard,
19 types::{
20 AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, DefaultConstructor,
21 Initializer, IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
22 },
23 vm::VirtualMachine,
24};
25use alloc::fmt;
26use core::cell::Cell;
27use core::ptr::NonNull;
28use rustpython_common::atomic::{Ordering, PyAtomic, Radium};
29use rustpython_common::lock::PyMutex;
30use rustpython_common::wtf8::Wtf8Buf;
31
32pub(crate) type DictContentType = dict_inner::Dict;
33
34#[pyclass(module = false, name = "dict", unhashable = true, traverse = "manual")]
35#[derive(Default)]
36#[repr(align(8))]
38pub struct PyDict {
39 entries: DictContentType,
40}
41pub type PyDictRef = PyRef<PyDict>;
42
43unsafe impl Traverse for PyDict {
45 fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
46 self.entries.traverse(traverse_fn);
47 }
48
49 fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
50 for (key, value) in self.entries.drain_entries() {
52 out.push(key);
53 out.push(value);
54 }
55 }
56}
57
58impl fmt::Debug for PyDict {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.write_str("dict")
62 }
63}
64
65thread_local! {
66 static DICT_FREELIST: Cell<crate::object::FreeList<PyDict>> = const { Cell::new(crate::object::FreeList::new()) };
67}
68
69impl PyPayload for PyDict {
70 const MAX_FREELIST: usize = 80;
71 const HAS_FREELIST: bool = true;
72
73 #[inline]
74 fn class(ctx: &Context) -> &'static Py<PyType> {
75 ctx.types.dict_type
76 }
77
78 #[inline]
79 unsafe fn freelist_push(obj: *mut PyObject) -> bool {
80 DICT_FREELIST
81 .try_with(|fl| {
82 let mut list = fl.take();
83 let stored = if list.len() < Self::MAX_FREELIST {
84 list.push(obj);
85 true
86 } else {
87 false
88 };
89 fl.set(list);
90 stored
91 })
92 .unwrap_or(false)
93 }
94
95 #[inline]
96 unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
97 DICT_FREELIST
98 .try_with(|fl| {
99 let mut list = fl.take();
100 let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
101 fl.set(list);
102 result
103 })
104 .ok()
105 .flatten()
106 }
107}
108
109impl PyDict {
110 #[deprecated(note = "use PyDict::default().into_ref() instead")]
111 pub fn new_ref(ctx: &Context) -> PyRef<Self> {
112 Self::default().into_ref(ctx)
113 }
114
115 pub(crate) const fn _as_dict_inner(&self) -> &DictContentType {
118 &self.entries
119 }
120
121 pub fn keys_vec(&self) -> Vec<PyObjectRef> {
124 self.entries.keys()
125 }
126
127 pub fn values_vec(&self) -> Vec<PyObjectRef> {
130 self.entries.values()
131 }
132
133 pub fn items_vec(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
136 self.entries.items()
137 }
138
139 fn merge_object_with_override(
140 &self,
141 other: PyObjectRef,
142 override_existing: bool,
143 vm: &VirtualMachine,
144 ) -> PyResult<()> {
145 let casted: Result<PyRefExact<Self>, _> = other.downcast_exact(vm);
146 let other = match casted {
147 Ok(dict_other) => {
148 return self.merge_dict(&dict_other, override_existing, vm);
149 }
150 Err(other) => other,
151 };
152 let dict = &self.entries;
153 let keys_result = other.get_attr(vm.ctx.intern_str("keys"), vm);
155 let has_keys = match keys_result {
156 Ok(keys_method) => {
157 let keys = PyIter::try_from_object(vm, keys_method.call((), vm)?)?;
158 while let PyIterReturn::Return(key) = keys.next(vm)? {
159 if !override_existing && dict.contains(vm, &*key)? {
160 continue;
161 }
162 let val = other.get_item(&*key, vm)?;
163 dict.insert(vm, &*key, val)?;
164 }
165 true
166 }
167 Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => false,
168 Err(e) => return Err(e),
169 };
170 if !has_keys {
171 return self.merge_from_seq2(&other, override_existing, vm);
172 }
173 Ok(())
174 }
175
176 pub fn merge_object(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
178 self.merge_object_with_override(other, true, vm)
179 }
180
181 pub fn merge_object_if_missing(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
182 self.merge_object_with_override(other, false, vm)
183 }
184
185 fn add_update_sequence_note(
186 exc: PyBaseExceptionRef,
187 index: usize,
188 vm: &VirtualMachine,
189 ) -> PyBaseExceptionRef {
190 if !exc.fast_isinstance(vm.ctx.exceptions.type_error) {
191 return exc;
192 }
193
194 let note =
195 format!("Cannot convert dictionary update sequence element #{index} to a sequence");
196 match vm.call_method(exc.as_object(), "add_note", (vm.ctx.new_str(note),)) {
197 Ok(_) => exc,
198 Err(note_err) => {
199 note_err.set_context(Some(exc));
200 note_err
201 }
202 }
203 }
204
205 fn update_sequence_pair_from_slice(
206 elements: &[PyObjectRef],
207 index: usize,
208 vm: &VirtualMachine,
209 ) -> PyResult<(PyObjectRef, PyObjectRef)> {
210 let [key, value] = elements else {
211 return Err(vm.new_value_error(format!(
212 "dictionary update sequence element #{index} has length {}; 2 is required",
213 elements.len()
214 )));
215 };
216 Ok((key.clone(), value.clone()))
217 }
218
219 pub(crate) fn update_sequence_pair(
220 element: PyObjectRef,
221 index: usize,
222 vm: &VirtualMachine,
223 ) -> PyResult<(PyObjectRef, PyObjectRef)> {
224 let element = match element.downcast_exact::<PyList>(vm) {
225 Ok(list) => {
226 let elements = list.borrow_vec();
227 return Self::update_sequence_pair_from_slice(&elements, index, vm);
228 }
229 Err(element) => element,
230 };
231 let element = match element.downcast_exact::<PyTuple>(vm) {
232 Ok(tuple) => {
233 return Self::update_sequence_pair_from_slice(tuple.as_slice(), index, vm);
234 }
235 Err(element) => element,
236 };
237
238 let elements = (|| {
239 let elem_iter = PyIter::try_from_object(vm, element).map_err(|exc| {
240 if exc.fast_isinstance(vm.ctx.exceptions.type_error) {
241 vm.new_type_error("object is not iterable")
242 } else {
243 exc
244 }
245 })?;
246 elem_iter
247 .into_iter::<PyObjectRef>(vm)
248 .collect::<PyResult<Vec<_>>>()
249 })()
250 .map_err(|exc| Self::add_update_sequence_note(exc, index, vm))?;
251
252 Self::update_sequence_pair_from_slice(&elements, index, vm)
253 }
254
255 pub fn merge_from_seq2(
256 &self,
257 seq2: &PyObject,
258 override_existing: bool,
259 vm: &VirtualMachine,
260 ) -> PyResult<()> {
261 let iter = seq2.get_iter(vm)?;
262 let dict = &self.entries;
263
264 for (index, element) in iter.iter::<PyObjectRef>(vm)?.enumerate() {
265 let (key, value) = Self::update_sequence_pair(element?, index, vm)?;
266
267 if !override_existing && dict.contains(vm, &*key)? {
268 continue;
269 }
270 dict.insert(vm, &*key, value)?;
271 }
272 Ok(())
273 }
274
275 pub(crate) fn merge_dict(
276 &self,
277 dict_other: &Py<Self>,
278 override_existing: bool,
279 vm: &VirtualMachine,
280 ) -> PyResult<()> {
281 let dict = &self.entries;
282 let dict_size = &dict_other.size();
283 let mut position = 0;
284 while let Some((next, key, value, hash)) = dict_other.entries.next_entry_with_hash(position)
285 {
286 position = next;
287 if !override_existing && dict.contains_known_hash(vm, &*key, hash)? {
288 continue;
289 }
290 dict.insert_known_hash(vm, &*key, hash, value)?;
291 }
292 if dict_other.entries.has_changed_size(dict_size) {
293 return Err(vm.new_runtime_error("dict mutated during update"));
294 }
295 Ok(())
296 }
297
298 pub fn is_empty(&self) -> bool {
299 self.entries.len() == 0
300 }
301
302 pub fn inner_setitem<K: DictKey + ?Sized>(
305 &self,
306 key: &K,
307 value: PyObjectRef,
308 vm: &VirtualMachine,
309 ) -> PyResult<()> {
310 self.entries.insert(vm, key, value)
311 }
312
313 pub(crate) fn inner_delitem<K: DictKey + ?Sized>(
314 &self,
315 key: &K,
316 vm: &VirtualMachine,
317 ) -> PyResult<()> {
318 self.entries.delete(vm, key)
319 }
320
321 pub fn get_or_insert(
322 &self,
323 vm: &VirtualMachine,
324 key: &PyObject,
325 default: impl FnOnce() -> PyObjectRef,
326 ) -> PyResult {
327 self.entries.setdefault(vm, key, default)
328 }
329
330 pub fn from_attributes(attrs: PyAttributes, vm: &VirtualMachine) -> PyResult<Self> {
331 let entries = DictContentType::default();
332
333 for (key, value) in attrs {
334 entries.insert(vm, key, value)?;
335 }
336
337 Ok(Self { entries })
338 }
339
340 pub fn contains_key<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> bool {
341 self.entries.contains(vm, key).unwrap()
342 }
343
344 pub fn size(&self) -> dict_inner::DictSize {
345 self.entries.size()
346 }
347
348 pub fn next_entry(&self, position: usize) -> Option<(usize, PyObjectRef, PyObjectRef)> {
349 self.entries.next_entry(position)
350 }
351
352 pub fn inner_getitem_opt<K: DictKey + ?Sized>(
353 &self,
354 key: &K,
355 vm: &VirtualMachine,
356 ) -> PyResult<Option<PyObjectRef>> {
357 self.entries.get(vm, key)
358 }
359
360 fn fromkeys_known_hashes(
364 obj: &PyObject,
365 vm: &VirtualMachine,
366 ) -> Option<Vec<(PyObjectRef, PyHash)>> {
367 if let Some(dict) = obj.downcast_ref_if_exact::<Self>(vm) {
368 Some(dict.entries.keys_with_hashes())
369 } else {
370 set::exact_set_keys_with_hashes(obj, vm)
371 }
372 }
373}
374
375#[derive(FromArgs)]
376struct DictGetArgs {
377 #[pyarg(positional)]
378 key: PyObjectRef,
379 #[pyarg(positional, optional)]
380 default: Option<PyObjectRef>,
381}
382
383#[derive(FromArgs)]
384struct FromKeysArgs {
385 #[pyarg(positional)]
386 iterable: ArgIterable,
387 #[pyarg(positional, optional)]
388 value: Option<PyObjectRef>,
389}
390
391impl PyDict {
393 pub fn __len__(&self) -> usize {
394 self.entries.len()
395 }
396
397 fn __contains__(&self, key: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
398 self.entries.contains(vm, key)
399 }
400
401 fn __delitem__(&self, key: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
402 self.inner_delitem(key, vm)
403 }
404
405 fn __setitem__(&self, key: &PyObject, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
406 self.inner_setitem(key, value, vm)
407 }
408
409 pub(crate) fn setdefault(
410 &self,
411 key: PyObjectRef,
412 default: PyObjectRef,
413 vm: &VirtualMachine,
414 ) -> PyResult {
415 self.entries.setdefault(vm, &*key, || default)
416 }
417
418 fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
419 if let Ok(other) = other.downcast::<Self>() {
422 let self_cp = self.copy();
423 self_cp.merge_dict(&other, true, vm)?;
424 return Ok(self_cp.into_pyobject(vm));
425 }
426 Ok(vm.ctx.not_implemented())
427 }
428
429 pub(crate) fn __sizeof__(&self) -> usize {
430 core::mem::size_of::<Self>() + self.entries.sizeof()
431 }
432 pub fn clear(&self) {
433 self.entries.clear()
434 }
435 #[must_use]
436 pub fn copy(&self) -> Self {
437 Self {
438 entries: self.entries.clone(),
439 }
440 }
441 pub(crate) fn update(
442 &self,
443 dict_obj: OptionalArg<PyObjectRef>,
444 kwargs: KwArgs,
445 vm: &VirtualMachine,
446 ) -> PyResult<()> {
447 if let OptionalArg::Present(dict_obj) = dict_obj {
448 self.merge_object(dict_obj, vm)?;
449 }
450 for (key, value) in kwargs {
451 self.entries.insert(vm, &key, value)?;
452 }
453 Ok(())
454 }
455}
456
457#[pyclass(
458 with(
459 Py,
460 PyRef,
461 Constructor,
462 Initializer,
463 Comparable,
464 Iterable,
465 AsSequence,
466 AsNumber,
467 AsMapping,
468 Representable
469 ),
470 flags(BASETYPE, MAPPING, _MATCH_SELF)
471)]
472impl PyDict {}
473
474#[pyclass]
475impl Py<PyDict> {
476 pub(crate) fn inner_cmp(
477 &self,
478 other: &Self,
479 op: PyComparisonOp,
480 item: bool,
481 vm: &VirtualMachine,
482 ) -> PyResult<PyComparisonValue> {
483 if op == PyComparisonOp::Ne {
484 return Self::inner_cmp(self, other, PyComparisonOp::Eq, item, vm)
485 .map(|x| x.map(|eq| !eq));
486 }
487 if !op.eval_ord(self.__len__().cmp(&other.__len__())) {
488 return Ok(PyArithmeticValue::Implemented(false));
489 }
490 let (superset, subset) = if self.__len__() < other.__len__() {
491 (other, self)
492 } else {
493 (self, other)
494 };
495 for (k, v1) in subset {
496 match superset.get_item_opt(&*k, vm)? {
497 Some(v2) => {
498 if v1.is(&v2) {
499 continue;
500 }
501 if item && !vm.bool_eq(&v1, &v2)? {
502 return Ok(PyArithmeticValue::Implemented(false));
503 }
504 }
505 None => {
506 return Ok(PyArithmeticValue::Implemented(false));
507 }
508 }
509 }
510 Ok(PyArithmeticValue::Implemented(true))
511 }
512
513 #[cfg_attr(feature = "flame-it", flame("PyDictRef"))]
514 #[pymethod(coexist)]
515 fn __getitem__(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
516 self.inner_getitem(&*key, vm)
517 }
518
519 fn __ror__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
520 let other_dict = other.downcast::<PyDict>();
521 if let Ok(other) = other_dict {
522 let other_cp = other.copy();
523 other_cp.merge_dict(self, true, vm)?;
524 return Ok(other_cp.into_pyobject(vm));
525 }
526 Ok(vm.ctx.not_implemented())
527 }
528
529 #[pyclassmethod]
530 fn fromkeys(class: PyTypeRef, args: FromKeysArgs, vm: &VirtualMachine) -> PyResult {
531 let FromKeysArgs { iterable, value } = args;
532 let value = value.unwrap_or_else(|| vm.ctx.none());
533 let d = PyType::call(&class, ().into(), vm)?;
534 match d.downcast_exact::<PyDict>(vm) {
535 Ok(pydict) => {
536 if let Some(keys) = PyDict::fromkeys_known_hashes(iterable.as_object(), vm) {
537 if class.is(vm.ctx.types.dict_type) {
538 pydict.entries.reserve_for_empty(keys.len());
539 }
540 for (key, hash) in keys {
541 pydict
542 .entries
543 .insert_known_hash(vm, &*key, hash, value.clone())?;
544 }
545 } else {
546 for key in iterable.iter(vm)? {
547 let key = key?;
548 pydict.__setitem__(&key, value.clone(), vm)?;
549 }
550 }
551 Ok(pydict.into_pyref().into())
552 }
553 Err(pyobj) => {
554 for key in iterable.iter(vm)? {
555 pyobj.set_item(&*key?, value.clone(), vm)?;
556 }
557 Ok(pyobj)
558 }
559 }
560 }
561
562 #[pymethod]
563 pub(crate) fn __sizeof__(&self) -> usize {
564 self.payload.__sizeof__()
565 }
566
567 #[pymethod]
568 pub fn clear(&self) {
569 self.payload.clear()
570 }
571
572 #[pymethod]
573 fn get(&self, args: DictGetArgs, vm: &VirtualMachine) -> PyResult {
574 Ok(self
575 .entries
576 .get(vm, &*args.key)?
577 .unwrap_or_else(|| args.default.unwrap_or_else(|| vm.ctx.none())))
578 }
579
580 #[pymethod(name = "setdefault")]
581 fn setdefault_py(&self, args: DictGetArgs, vm: &VirtualMachine) -> PyResult {
582 let default = args.default.unwrap_or_else(|| vm.ctx.none());
583 self.setdefault(args.key, default, vm)
584 }
585
586 #[pymethod]
587 #[must_use]
588 pub fn copy(&self) -> PyDict {
589 self.payload.copy()
590 }
591
592 #[pymethod]
593 pub(crate) fn update(
594 &self,
595 dict_obj: OptionalArg<PyObjectRef>,
596 kwargs: KwArgs,
597 vm: &VirtualMachine,
598 ) -> PyResult<()> {
599 self.payload.update(dict_obj, kwargs, vm)
600 }
601
602 #[pymethod]
603 fn pop(
604 &self,
605 key: PyObjectRef,
606 default: OptionalArg<PyObjectRef>,
607 vm: &VirtualMachine,
608 ) -> PyResult {
609 match self.entries.pop(vm, &*key)? {
610 Some(value) => Ok(value),
611 None => default.ok_or_else(|| vm.new_key_error(key)),
612 }
613 }
614
615 #[pymethod]
616 fn popitem(&self, vm: &VirtualMachine) -> PyResult<(PyObjectRef, PyObjectRef)> {
617 let (key, value) = self.entries.pop_back().ok_or_else(|| {
618 let err_msg = vm
619 .ctx
620 .new_str(ascii!("popitem(): dictionary is empty"))
621 .into();
622 vm.new_key_error(err_msg)
623 })?;
624 Ok((key, value))
625 }
626
627 #[pyclassmethod]
628 fn __class_getitem__(
629 cls: PyTypeRef,
630 object: PyObjectRef,
631 vm: &VirtualMachine,
632 ) -> PyResult<PyGenericAlias> {
633 PyGenericAlias::from_args(cls, object, vm)
634 }
635}
636
637#[pyclass]
638impl PyRef<PyDict> {
639 #[pymethod]
640 const fn keys(self) -> PyDictKeys {
641 PyDictKeys::new(self)
642 }
643
644 #[pymethod]
645 const fn values(self) -> PyDictValues {
646 PyDictValues::new(self)
647 }
648
649 #[pymethod]
650 const fn items(self) -> PyDictItems {
651 PyDictItems::new(self)
652 }
653
654 #[pymethod]
655 fn __reversed__(self) -> PyDictReverseKeyIterator {
656 PyDictReverseKeyIterator::new(self)
657 }
658
659 fn __ior__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
660 self.merge_object(other, vm)?;
661 Ok(self)
662 }
663}
664
665impl DefaultConstructor for PyDict {}
666
667impl Initializer for PyDict {
668 type Args = (OptionalArg<PyObjectRef>, KwArgs);
669
670 fn init(zelf: &Py<Self>, (dict_obj, kwargs): Self::Args, vm: &VirtualMachine) -> PyResult<()> {
671 zelf.update(dict_obj, kwargs, vm)
672 }
673}
674
675impl AsMapping for PyDict {
676 fn as_mapping() -> &'static PyMappingMethods {
677 static AS_MAPPING: PyMappingMethods = PyMappingMethods {
678 length: atomic_func!(|mapping, _vm| Ok(PyDict::mapping_downcast(mapping).__len__())),
679 subscript: atomic_func!(|mapping, needle, vm| {
680 PyDict::mapping_downcast(mapping).inner_getitem(needle, vm)
681 }),
682 ass_subscript: atomic_func!(|mapping, needle, value, vm| {
683 let zelf = PyDict::mapping_downcast(mapping);
684 if let Some(value) = value {
685 zelf.inner_setitem(needle, value, vm)?;
686 if zelf.as_object().is(vm.builtins.dict().as_object()) {
687 crate::stdlib::_testinternalcapi::note_builtin_dict();
688 }
689 Ok(())
690 } else {
691 zelf.inner_delitem(needle, vm)
692 }
693 }),
694 };
695 &AS_MAPPING
696 }
697}
698
699impl AsSequence for PyDict {
700 fn as_sequence() -> &'static PySequenceMethods {
701 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
702 contains: atomic_func!(|seq, target, vm| PyDict::sequence_downcast(seq)
703 .entries
704 .contains(vm, target)),
705 ..PySequenceMethods::NOT_IMPLEMENTED
706 });
707 &AS_SEQUENCE
708 }
709}
710
711impl AsNumber for PyDict {
712 fn as_number() -> &'static PyNumberMethods {
713 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
714 or: Some(|a, b, vm| {
715 if let Some(a) = a.downcast_ref::<PyDict>() {
716 PyDict::__or__(a, b.to_pyobject(vm), vm)
717 } else {
718 Ok(vm.ctx.not_implemented())
719 }
720 }),
721 inplace_or: Some(|a, b, vm| {
722 if let Some(a) = a.downcast_ref::<PyDict>() {
723 a.to_owned()
724 .__ior__(b.to_pyobject(vm), vm)
725 .map(|d| d.into())
726 } else {
727 Ok(vm.ctx.not_implemented())
728 }
729 }),
730 ..PyNumberMethods::NOT_IMPLEMENTED
731 };
732 &AS_NUMBER
733 }
734}
735
736impl Comparable for PyDict {
737 fn cmp(
738 zelf: &Py<Self>,
739 other: &PyObject,
740 op: PyComparisonOp,
741 vm: &VirtualMachine,
742 ) -> PyResult<PyComparisonValue> {
743 op.eq_only(|| {
744 let other = class_or_notimplemented!(Self, other);
745 zelf.inner_cmp(other, PyComparisonOp::Eq, true, vm)
746 })
747 }
748}
749
750impl Iterable for PyDict {
751 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
752 Ok(PyDictKeyIterator::new(zelf).into_pyobject(vm))
753 }
754}
755
756impl Representable for PyDict {
757 #[inline]
758 fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
759 let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
760 let mut result = Wtf8Buf::from("{");
761 let mut first = true;
762 for (key, value) in zelf {
763 if !first {
764 result.push_str(", ");
765 }
766 first = false;
767 result.push_wtf8(key.repr(vm)?.as_wtf8());
768 result.push_str(": ");
769 result.push_wtf8(value.repr(vm)?.as_wtf8());
770 }
771 result.push_char('}');
772 vm.ctx.new_str(result)
773 } else {
774 vm.ctx.intern_str("{...}").to_owned()
775 };
776 Ok(s)
777 }
778
779 #[cold]
780 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
781 unreachable!("use repr instead")
782 }
783}
784
785impl Py<PyDict> {
786 #[inline]
787 fn exact_dict(&self, vm: &VirtualMachine) -> bool {
788 self.class().is(vm.ctx.types.dict_type)
789 }
790
791 fn missing_opt<K: DictKey + ?Sized>(
792 &self,
793 key: &K,
794 vm: &VirtualMachine,
795 ) -> PyResult<Option<PyObjectRef>> {
796 vm.get_method(self.to_owned().into(), identifier!(vm, __missing__))
797 .map(|methods| methods?.call((key.to_pyobject(vm),), vm))
798 .transpose()
799 }
800
801 #[inline]
802 fn inner_getitem<K: DictKey + ?Sized>(
803 &self,
804 key: &K,
805 vm: &VirtualMachine,
806 ) -> PyResult<PyObjectRef> {
807 if let Some(value) = self.entries.get(vm, key)? {
808 Ok(value)
809 } else if let Some(value) = self.missing_opt(key, vm)? {
810 Ok(value)
811 } else {
812 Err(vm.new_key_error(key.to_pyobject(vm)))
813 }
814 }
815
816 pub fn to_attributes(
823 &self,
824 vm: &VirtualMachine,
825 on_non_string: impl FnOnce(&VirtualMachine) -> PyResult<()>,
826 ) -> PyResult<PyAttributes> {
827 let mut attrs = PyAttributes::default();
828 let mut on_non_string = Some(on_non_string);
829 for (key, value) in self {
830 let key = match key.downcast_exact::<PyStr>(vm) {
831 Ok(key) => vm.ctx.intern_str(key),
832 Err(key) => match key.downcast_ref::<PyStr>() {
837 Some(key) => vm.ctx.intern_str(key.as_wtf8()),
838 None => {
839 if let Some(on_non_string) = on_non_string.take() {
840 on_non_string(vm)?;
841 }
842 continue;
843 }
844 },
845 };
846 attrs.insert(key, value);
847 }
848 Ok(attrs)
849 }
850
851 pub(crate) fn get_item_known_hash(
852 &self,
853 key: &PyObject,
854 hash: crate::common::hash::PyHash,
855 vm: &VirtualMachine,
856 ) -> PyResult<Option<PyObjectRef>> {
857 self.entries.get_known_hash(vm, key, hash)
858 }
859
860 pub(crate) fn set_item_known_hash(
861 &self,
862 key: &PyObject,
863 hash: crate::common::hash::PyHash,
864 value: PyObjectRef,
865 vm: &VirtualMachine,
866 ) -> PyResult<()> {
867 self.entries.insert_known_hash(vm, key, hash, value)
868 }
869
870 pub(crate) fn del_item_known_hash(
871 &self,
872 key: &PyObject,
873 hash: crate::common::hash::PyHash,
874 vm: &VirtualMachine,
875 ) -> PyResult<()> {
876 if self.entries.delete_if_exists_known_hash(vm, key, hash)? {
877 Ok(())
878 } else {
879 Err(vm.new_key_error(key.to_owned()))
880 }
881 }
882
883 pub(crate) fn contains_known_hash(
884 &self,
885 key: &PyObject,
886 hash: crate::common::hash::PyHash,
887 vm: &VirtualMachine,
888 ) -> PyResult<bool> {
889 self.entries.contains_known_hash(vm, key, hash)
890 }
891
892 pub fn get_item_opt<K: DictKey + ?Sized>(
893 &self,
894 key: &K,
895 vm: &VirtualMachine,
896 ) -> PyResult<Option<PyObjectRef>> {
897 if self.exact_dict(vm) {
898 self.entries.get(vm, key)
899 } else {
902 match self.as_object().get_item(key, vm) {
903 Ok(value) => Ok(Some(value)),
904 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
905 self.missing_opt(key, vm)
906 }
907 Err(e) => Err(e),
908 }
909 }
910 }
911
912 pub(crate) fn hint_for_key<K: DictKey + ?Sized>(
914 &self,
915 key: &K,
916 vm: &VirtualMachine,
917 ) -> PyResult<Option<u16>> {
918 if self.exact_dict(vm) {
919 self.entries.hint_for_key(vm, key)
920 } else {
921 Ok(None)
922 }
923 }
924
925 #[inline]
927 pub(crate) fn get_item_by_index_and_keys_version(
928 &self,
929 version: u16,
930 index: u16,
931 ) -> Option<PyObjectRef> {
932 self.entries
933 .get_index_if_keys_version(u32::from(version), usize::from(index))
934 }
935
936 pub(crate) fn get_item_opt_refresh_hint<K: DictKey + ?Sized>(
941 &self,
942 key: &K,
943 hint: u16,
944 vm: &VirtualMachine,
945 ) -> PyResult<Option<(PyObjectRef, Option<u16>)>> {
946 if self.exact_dict(vm) {
947 if let Some(value) = self.entries.get_hint(vm, key, usize::from(hint))? {
948 return Ok(Some((value, None)));
949 }
950 self.entries.get_with_hint(vm, key)
951 } else {
952 Ok(self.get_item_opt(key, vm)?.map(|value| (value, None)))
953 }
954 }
955
956 pub(crate) fn set_item_with_hint<K: DictKey + ?Sized>(
961 &self,
962 key: &K,
963 hint: u16,
964 value: PyObjectRef,
965 vm: &VirtualMachine,
966 ) -> PyResult<Option<u16>> {
967 if self.exact_dict(vm) {
968 self.entries
969 .insert_with_hint(vm, key, usize::from(hint), value)
970 } else {
971 self.as_object().set_item(key, value, vm)?;
972 Ok(None)
973 }
974 }
975
976 pub(crate) fn keys_version(&self) -> u32 {
978 self.entries.keys_version()
979 }
980
981 pub(crate) fn module_attr_cache(
982 &self,
983 name: &super::PyStrInterned,
984 vm: &VirtualMachine,
985 ) -> Option<(u32, u16)> {
986 self.exact_dict(vm)
987 .then(|| self.entries.module_attr_cache(name, vm))
988 .flatten()
989 }
990
991 #[inline]
992 pub(crate) fn get_cached_module_attr(
993 &self,
994 name: &super::PyStrInterned,
995 version: usize,
996 index: usize,
997 vm: &VirtualMachine,
998 ) -> Option<PyObjectRef> {
999 self.exact_dict(vm)
1000 .then(|| {
1001 self.entries
1002 .get_cached_module_attr(name, version, index, vm)
1003 })
1004 .flatten()
1005 }
1006
1007 pub(crate) fn assign_keys_version(&self, vm: &VirtualMachine) -> u32 {
1012 if self.exact_dict(vm) {
1013 self.entries.assign_keys_version()
1014 } else {
1015 0
1016 }
1017 }
1018
1019 pub fn get_item<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> PyResult {
1020 if self.exact_dict(vm) {
1021 self.inner_getitem(key, vm)
1022 } else {
1023 self.as_object().get_item(key, vm)
1024 }
1025 }
1026
1027 pub fn set_item<K: DictKey + ?Sized>(
1028 &self,
1029 key: &K,
1030 value: PyObjectRef,
1031 vm: &VirtualMachine,
1032 ) -> PyResult<()> {
1033 if self.exact_dict(vm) {
1034 self.inner_setitem(key, value, vm)?;
1035 if self.as_object().is(vm.builtins.dict().as_object()) {
1036 crate::stdlib::_testinternalcapi::note_builtin_dict();
1037 }
1038 Ok(())
1039 } else {
1040 self.as_object().set_item(key, value, vm)
1041 }
1042 }
1043
1044 pub fn del_item<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> PyResult<()> {
1045 if self.exact_dict(vm) {
1046 self.inner_delitem(key, vm)
1047 } else {
1048 self.as_object().del_item(key, vm)
1049 }
1050 }
1051
1052 pub fn pop_item<K: DictKey + ?Sized>(
1053 &self,
1054 key: &K,
1055 vm: &VirtualMachine,
1056 ) -> PyResult<Option<PyObjectRef>> {
1057 if self.exact_dict(vm) {
1058 self.entries.remove_if_exists(vm, key)
1059 } else {
1060 let value = self.as_object().get_item(key, vm)?;
1061 self.as_object().del_item(key, vm)?;
1062 Ok(Some(value))
1063 }
1064 }
1065
1066 pub fn get_chain<K: DictKey + ?Sized>(
1067 &self,
1068 other: &Self,
1069 key: &K,
1070 vm: &VirtualMachine,
1071 ) -> PyResult<Option<PyObjectRef>> {
1072 let self_exact = self.exact_dict(vm);
1073 let other_exact = other.exact_dict(vm);
1074 if self_exact && other_exact {
1075 let self_exact = unsafe { PyExact::ref_unchecked(self) };
1077 let other_exact = unsafe { PyExact::ref_unchecked(other) };
1078 self_exact.get_chain_exact(other_exact, key, vm)
1079 } else if let Some(value) = self.get_item_opt(key, vm)? {
1080 Ok(Some(value))
1081 } else {
1082 other.get_item_opt(key, vm)
1083 }
1084 }
1085}
1086
1087impl PyExact<PyDict> {
1088 pub(crate) fn get_chain_exact<K: DictKey + ?Sized>(
1091 &self,
1092 other: &Self,
1093 key: &K,
1094 vm: &VirtualMachine,
1095 ) -> PyResult<Option<PyObjectRef>> {
1096 debug_assert!(self.class().is(vm.ctx.types.dict_type));
1097 debug_assert!(other.class().is(vm.ctx.types.dict_type));
1098 self.entries.get_chain(&other.entries, vm, key)
1099 }
1100}
1101
1102impl IntoIterator for PyDictRef {
1104 type Item = (PyObjectRef, PyObjectRef);
1105 type IntoIter = DictIntoIter;
1106
1107 fn into_iter(self) -> Self::IntoIter {
1108 DictIntoIter::new(self)
1109 }
1110}
1111
1112impl<'a> IntoIterator for &'a PyDictRef {
1113 type Item = (PyObjectRef, PyObjectRef);
1114 type IntoIter = DictIter<'a>;
1115
1116 fn into_iter(self) -> Self::IntoIter {
1117 DictIter::new(self)
1118 }
1119}
1120
1121impl<'a> IntoIterator for &'a Py<PyDict> {
1122 type Item = (PyObjectRef, PyObjectRef);
1123 type IntoIter = DictIter<'a>;
1124
1125 fn into_iter(self) -> Self::IntoIter {
1126 DictIter::new(self)
1127 }
1128}
1129
1130pub struct DictIntoIter {
1131 dict: PyDictRef,
1132 position: usize,
1133}
1134
1135impl DictIntoIter {
1136 pub const fn new(dict: PyDictRef) -> Self {
1137 Self { dict, position: 0 }
1138 }
1139}
1140
1141impl Iterator for DictIntoIter {
1142 type Item = (PyObjectRef, PyObjectRef);
1143
1144 fn next(&mut self) -> Option<Self::Item> {
1145 let (position, key, value) = self.dict.entries.next_entry(self.position)?;
1146 self.position = position;
1147 Some((key, value))
1148 }
1149
1150 fn size_hint(&self) -> (usize, Option<usize>) {
1151 let l = self.len();
1152 (l, Some(l))
1153 }
1154}
1155
1156impl ExactSizeIterator for DictIntoIter {
1157 fn len(&self) -> usize {
1158 self.dict.entries.len_from_entry_index(self.position)
1159 }
1160}
1161
1162pub struct DictIter<'a> {
1163 dict: &'a Py<PyDict>,
1164 position: usize,
1165}
1166
1167impl<'a> DictIter<'a> {
1168 pub const fn new(dict: &'a Py<PyDict>) -> Self {
1169 DictIter { dict, position: 0 }
1170 }
1171}
1172
1173impl Iterator for DictIter<'_> {
1174 type Item = (PyObjectRef, PyObjectRef);
1175
1176 fn next(&mut self) -> Option<Self::Item> {
1177 let (position, key, value) = self.dict.entries.next_entry(self.position)?;
1178 self.position = position;
1179 Some((key, value))
1180 }
1181
1182 fn size_hint(&self) -> (usize, Option<usize>) {
1183 let l = self.len();
1184 (l, Some(l))
1185 }
1186}
1187
1188impl ExactSizeIterator for DictIter<'_> {
1189 fn len(&self) -> usize {
1190 self.dict.entries.len_from_entry_index(self.position)
1191 }
1192}
1193
1194#[pyclass]
1195trait DictView: PyPayload + PyClassDef + Iterable + Representable {
1196 type ReverseIter: PyPayload + core::fmt::Debug;
1197
1198 fn dict(&self) -> &Py<PyDict>;
1199 fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef;
1200
1201 fn __len__(&self) -> usize {
1202 self.dict().__len__()
1203 }
1204
1205 #[pymethod]
1206 fn __reversed__(zelf: &Py<Self>) -> Self::ReverseIter;
1207}
1208
1209macro_rules! dict_view {
1210 (
1211 $name: ident,
1212 $iter_name: ident,
1213 $reverse_iter_name: ident,
1214 $class: ident,
1215 $iter_class: ident,
1216 $reverse_iter_class: ident,
1217 $class_name: literal,
1218 $iter_class_name: literal,
1219 $reverse_iter_class_name: literal,
1220 $unhashable: literal,
1221 $project_fn: expr,
1222 $result_fn: expr
1223 ) => {
1224 #[pyclass(module = false, name = $class_name, unhashable = $unhashable)]
1225 #[derive(Debug)]
1226 pub(crate) struct $name {
1227 pub(crate) dict: PyDictRef,
1228 }
1229
1230 impl $name {
1231 pub(crate) const fn new(dict: PyDictRef) -> Self {
1232 $name { dict }
1233 }
1234 }
1235
1236 impl DictView for $name {
1237 type ReverseIter = $reverse_iter_name;
1238
1239 fn dict(&self) -> &Py<PyDict> {
1240 &self.dict
1241 }
1242
1243 fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef {
1244 $result_fn(vm, $project_fn(&key, &value))
1245 }
1246
1247 fn __reversed__(zelf: &Py<Self>) -> Self::ReverseIter {
1248 $reverse_iter_name::new(zelf.dict.clone())
1249 }
1250 }
1251
1252 impl Iterable for $name {
1253 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
1254 Ok($iter_name::new(zelf.dict.clone()).into_pyobject(vm))
1255 }
1256 }
1257
1258 impl PyPayload for $name {
1259 fn class(ctx: &Context) -> &'static Py<PyType> {
1260 ctx.types.$class
1261 }
1262 }
1263
1264 impl Representable for $name {
1265 #[inline]
1266 fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1267 let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
1268 let mut result = Wtf8Buf::from(format!("{}([", Self::NAME));
1269 let mut first = true;
1270 for (key, value) in zelf.dict().clone() {
1271 if !first {
1272 result.push_str(", ");
1273 }
1274 first = false;
1275 result.push_wtf8(Self::item(vm, key, value).repr(vm)?.as_wtf8());
1276 }
1277 result.push_str("])");
1278 vm.ctx.new_str(result)
1279 } else {
1280 vm.ctx.intern_str("{...}").to_owned()
1281 };
1282 Ok(s)
1283 }
1284
1285 #[cold]
1286 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1287 unreachable!("use repr instead")
1288 }
1289 }
1290
1291 #[pyclass(module = false, name = $iter_class_name)]
1292 #[derive(Debug)]
1293 pub(crate) struct $iter_name {
1294 pub(crate) size: dict_inner::DictSize,
1295 changed: PyAtomic<bool>,
1300 pub(crate) internal: PyMutex<PositionIterInternal<PyDictRef>>,
1301 }
1302
1303 impl PyPayload for $iter_name {
1304 #[inline]
1305 fn class(ctx: &Context) -> &'static Py<PyType> {
1306 ctx.types.$iter_class
1307 }
1308 }
1309
1310 impl $iter_name {
1311 fn new(dict: PyDictRef) -> Self {
1312 $iter_name {
1313 size: dict.size(),
1314 changed: Radium::new(false),
1315 internal: PyMutex::new(PositionIterInternal::new(dict, 0)),
1316 }
1317 }
1318 }
1319
1320 #[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1321 impl Py<$iter_name> {
1322 #[pymethod]
1323 fn __length_hint__(&self) -> usize {
1324 if self.changed.load(Ordering::Relaxed) {
1328 return 0;
1329 }
1330 self.internal.lock().length_hint(|dict| {
1331 if dict.size() == self.size {
1332 self.size.entries_size
1333 } else {
1334 0
1335 }
1336 })
1337 }
1338
1339 #[pymethod]
1340 fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
1341 let iter = builtins_iter(vm)?;
1342 let internal = self.internal.lock();
1343 let entries = match &internal.status {
1344 IterStatus::Active(dict) => {
1345 let mut position = internal.position;
1346 let mut entries = Vec::new();
1347 while let Some((next_position, key, value)) =
1348 dict.entries.next_entry(position)
1349 {
1350 entries.push(($result_fn)(vm, ($project_fn)(&key, &value)));
1351 position = next_position;
1352 }
1353 entries
1354 }
1355 IterStatus::Exhausted => vec![],
1356 };
1357 Ok(vm.new_tuple((iter, (vm.ctx.new_list(entries),))))
1358 }
1359 }
1360
1361 impl SelfIter for $iter_name {}
1362
1363 impl IterNext for $iter_name {
1364 fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1365 locked_step(&zelf.internal, |internal| {
1366 let IterStatus::Active(dict) = &internal.status else {
1367 return (Ok(PyIterReturn::StopIteration(None)), None);
1368 };
1369 let mutated =
1370 || vm.new_runtime_error("dictionary changed size during iteration");
1371 if zelf.changed.load(Ordering::Relaxed) {
1372 return (Err(mutated()), None);
1376 }
1377 let entry =
1378 dict.entries
1379 .next_entry_checked(internal.position, &zelf.size, $project_fn);
1380 match entry {
1381 Err(dict_inner::DictChanged) => {
1382 zelf.changed.store(true, Ordering::Relaxed);
1383 (Err(mutated()), None)
1384 }
1385 Ok(Some((position, item))) => {
1386 internal.position = position;
1387 (Ok(PyIterReturn::Return(($result_fn)(vm, item))), None)
1388 }
1389 Ok(None) => (Ok(PyIterReturn::StopIteration(None)), internal.exhaust()),
1390 }
1391 })
1392 }
1393 }
1394
1395 #[pyclass(module = false, name = $reverse_iter_class_name)]
1396 #[derive(Debug)]
1397 pub(crate) struct $reverse_iter_name {
1398 pub(crate) size: dict_inner::DictSize,
1399 changed: PyAtomic<bool>,
1401 internal: PyMutex<PositionIterInternal<PyDictRef>>,
1402 }
1403
1404 impl PyPayload for $reverse_iter_name {
1405 #[inline]
1406 fn class(ctx: &Context) -> &'static Py<PyType> {
1407 ctx.types.$reverse_iter_class
1408 }
1409 }
1410
1411 impl $reverse_iter_name {
1412 fn new(dict: PyDictRef) -> Self {
1413 let size = dict.size();
1414 let position = size.entries_size.saturating_sub(1);
1415 $reverse_iter_name {
1416 size,
1417 changed: Radium::new(false),
1418 internal: PyMutex::new(PositionIterInternal::new(dict, position)),
1419 }
1420 }
1421 }
1422
1423 #[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1424 impl Py<$reverse_iter_name> {
1425 #[pymethod]
1426 fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
1427 let iter = builtins_iter(vm)?;
1428 let internal = self.internal.lock();
1429 let entries = match &internal.status {
1430 IterStatus::Active(dict) => {
1431 let mut position = internal.position;
1432 let mut entries = Vec::new();
1433 while let Some((found_index, key, value)) =
1434 dict.entries.prev_entry(position)
1435 {
1436 entries.push(($result_fn)(vm, ($project_fn)(&key, &value)));
1437 if found_index == 0 {
1438 break;
1439 }
1440 position = found_index - 1;
1441 }
1442 entries
1443 }
1444 IterStatus::Exhausted => vec![],
1445 };
1446 Ok(vm.new_tuple((iter, (vm.ctx.new_list(entries),))))
1447 }
1448
1449 #[pymethod]
1450 fn __length_hint__(&self) -> usize {
1451 if self.changed.load(Ordering::Relaxed) {
1453 return 0;
1454 }
1455 let internal = self.internal.lock();
1456 match &internal.status {
1457 IterStatus::Active(dict) if dict.size() == self.size => {
1458 internal.rev_length_hint(|_| self.size.entries_size)
1459 }
1460 _ => 0,
1461 }
1462 }
1463 }
1464
1465 impl SelfIter for $reverse_iter_name {}
1466
1467 impl IterNext for $reverse_iter_name {
1468 fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1469 locked_step(&zelf.internal, |internal| {
1470 let IterStatus::Active(dict) = &internal.status else {
1471 return (Ok(PyIterReturn::StopIteration(None)), None);
1472 };
1473 let mutated =
1474 || vm.new_runtime_error("dictionary changed size during iteration");
1475 if zelf.changed.load(Ordering::Relaxed) {
1476 return (Err(mutated()), None);
1480 }
1481 let entry =
1482 dict.entries
1483 .prev_entry_checked(internal.position, &zelf.size, $project_fn);
1484 match entry {
1485 Err(dict_inner::DictChanged) => {
1486 zelf.changed.store(true, Ordering::Relaxed);
1487 (Err(mutated()), None)
1488 }
1489 Ok(Some((found_index, item))) => {
1490 let released = if found_index == 0 {
1491 internal.exhaust()
1492 } else {
1493 internal.position = found_index - 1;
1494 None
1495 };
1496 (Ok(PyIterReturn::Return(($result_fn)(vm, item))), released)
1497 }
1498 Ok(None) => (Ok(PyIterReturn::StopIteration(None)), internal.exhaust()),
1499 }
1500 })
1501 }
1502 }
1503 };
1504}
1505
1506dict_view! {
1507 PyDictKeys,
1508 PyDictKeyIterator,
1509 PyDictReverseKeyIterator,
1510 dict_keys_type,
1511 dict_keyiterator_type,
1512 dict_reversekeyiterator_type,
1513 "dict_keys",
1514 "dict_keyiterator",
1515 "dict_reversekeyiterator",
1516 true,
1517 |key: &PyObject, _value| key.to_owned(),
1518 |_vm: &VirtualMachine, key: PyObjectRef| key
1519}
1520
1521dict_view! {
1522 PyDictValues,
1523 PyDictValueIterator,
1524 PyDictReverseValueIterator,
1525 dict_values_type,
1526 dict_valueiterator_type,
1527 dict_reversevalueiterator_type,
1528 "dict_values",
1529 "dict_valueiterator",
1530 "dict_reversevalueiterator",
1531 false,
1532 |_key: &PyObject, value: &PyObjectRef| value.clone(),
1533 |_vm: &VirtualMachine, value: PyObjectRef| value
1534}
1535
1536dict_view! {
1537 PyDictItems,
1538 PyDictItemIterator,
1539 PyDictReverseItemIterator,
1540 dict_items_type,
1541 dict_itemiterator_type,
1542 dict_reverseitemiterator_type,
1543 "dict_items",
1544 "dict_itemiterator",
1545 "dict_reverseitemiterator",
1546 true,
1547 |key: &PyObject, value: &PyObjectRef| (key.to_owned(), value.clone()),
1548 |vm: &VirtualMachine, (key, value): (PyObjectRef, PyObjectRef)|
1550 vm.new_tuple((key, value)).into()
1551}
1552
1553fn is_set_or_dict_view(obj: &PyObject) -> bool {
1554 obj.downcast_ref::<PySet>().is_some()
1555 || obj.downcast_ref::<PyFrozenSet>().is_some()
1556 || obj.downcast_ref::<PyDictKeys>().is_some()
1557 || obj.downcast_ref::<PyDictItems>().is_some()
1558}
1559
1560#[pyclass]
1562trait ViewSetOps: DictView {
1563 fn cmp(
1564 zelf: &Py<Self>,
1565 other: &PyObject,
1566 op: PyComparisonOp,
1567 vm: &VirtualMachine,
1568 ) -> PyResult<PyComparisonValue> {
1569 if let Some(dictview) = other.downcast_ref::<Self>() {
1570 return zelf.dict().inner_cmp(
1571 dictview.dict(),
1572 op,
1573 !zelf.class().is(vm.ctx.types.dict_keys_type),
1574 vm,
1575 );
1576 }
1577 if !is_set_or_dict_view(other) {
1578 return Ok(PyComparisonValue::NotImplemented);
1579 }
1580 if op == PyComparisonOp::Ne {
1581 return ViewSetOps::cmp(zelf, other, PyComparisonOp::Eq, vm)
1582 .map(|result| result.map(|equal| !equal));
1583 }
1584 if !op.eval_ord(zelf.__len__().cmp(&other.length(vm)?)) {
1585 return Ok(PyComparisonValue::Implemented(false));
1586 }
1587 let (subset, superset) = if matches!(op, PyComparisonOp::Gt | PyComparisonOp::Ge) {
1588 (other, zelf.as_object())
1589 } else {
1590 (zelf.as_object(), other)
1591 };
1592 let subset = ArgIterable::<PyObjectRef>::try_from_object(vm, subset.to_owned())?;
1593 for item in subset.iter(vm)? {
1594 let item = item?;
1595 if !superset.sequence_unchecked().contains(&item, vm)? {
1596 return Ok(PyComparisonValue::Implemented(false));
1597 }
1598 }
1599 Ok(PyComparisonValue::Implemented(true))
1600 }
1601
1602 #[pymethod]
1603 fn isdisjoint(zelf: PyRef<Self>, object: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
1604 if zelf.is(object.as_object()) {
1605 return Ok(zelf.__len__() == 0);
1606 }
1607 let other_is_larger = if is_set_or_dict_view(object.as_object()) {
1608 let len = zelf.__len__();
1609 object.as_object().length(vm)? > len
1610 } else {
1611 false
1612 };
1613 let (container, iterable) = if other_is_larger {
1614 (object.as_object(), zelf.as_object())
1615 } else {
1616 (zelf.as_object(), object.as_object())
1617 };
1618 let iterable = ArgIterable::<PyObjectRef>::try_from_object(vm, iterable.to_owned())?;
1619 for item in iterable.iter(vm)? {
1620 let item = item?;
1621 if container.sequence_unchecked().contains(&item, vm)? {
1622 return Ok(false);
1623 }
1624 }
1625 Ok(true)
1626 }
1627}
1628
1629impl ViewSetOps for PyDictKeys {}
1630
1631#[pyclass(
1632 flags(DISALLOW_INSTANTIATION),
1633 with(
1634 DictView,
1635 Comparable,
1636 Iterable,
1637 ViewSetOps,
1638 AsSequence,
1639 AsNumber,
1640 Representable
1641 )
1642)]
1643impl PyDictKeys {
1644 fn __contains__(zelf: &PyObject, key: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
1645 zelf.sequence_unchecked().contains(key, vm)
1646 }
1647
1648 #[pygetset]
1649 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1650 PyMappingProxy::from(zelf.dict().to_owned())
1651 }
1652}
1653
1654impl Comparable for PyDictKeys {
1655 fn cmp(
1656 zelf: &Py<Self>,
1657 other: &PyObject,
1658 op: PyComparisonOp,
1659 vm: &VirtualMachine,
1660 ) -> PyResult<PyComparisonValue> {
1661 ViewSetOps::cmp(zelf, other, op, vm)
1662 }
1663}
1664
1665impl AsSequence for PyDictKeys {
1666 fn as_sequence() -> &'static PySequenceMethods {
1667 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1668 length: atomic_func!(|seq, _vm| Ok(PyDictKeys::sequence_downcast(seq).__len__())),
1669 contains: atomic_func!(|seq, target, vm| {
1670 PyDictKeys::sequence_downcast(seq)
1671 .dict
1672 .entries
1673 .contains(vm, target)
1674 }),
1675 ..PySequenceMethods::NOT_IMPLEMENTED
1676 });
1677 &AS_SEQUENCE
1678 }
1679}
1680
1681impl AsNumber for PyDictKeys {
1682 fn as_number() -> &'static PyNumberMethods {
1683 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1684 subtract: Some(set_inner_number_subtract),
1685 and: Some(set_inner_number_and),
1686 xor: Some(set_inner_number_xor),
1687 or: Some(set_inner_number_or),
1688 ..PyNumberMethods::NOT_IMPLEMENTED
1689 };
1690 &AS_NUMBER
1691 }
1692}
1693
1694impl ViewSetOps for PyDictItems {}
1695#[pyclass(
1696 flags(DISALLOW_INSTANTIATION),
1697 with(
1698 DictView,
1699 Comparable,
1700 Iterable,
1701 ViewSetOps,
1702 AsSequence,
1703 AsNumber,
1704 Representable
1705 )
1706)]
1707impl PyDictItems {
1708 fn __contains__(zelf: &PyObject, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
1709 zelf.sequence_unchecked().contains(needle, vm)
1710 }
1711 #[pygetset]
1712 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1713 PyMappingProxy::from(zelf.dict().to_owned())
1714 }
1715}
1716
1717impl Comparable for PyDictItems {
1718 fn cmp(
1719 zelf: &Py<Self>,
1720 other: &PyObject,
1721 op: PyComparisonOp,
1722 vm: &VirtualMachine,
1723 ) -> PyResult<PyComparisonValue> {
1724 ViewSetOps::cmp(zelf, other, op, vm)
1725 }
1726}
1727
1728impl AsSequence for PyDictItems {
1729 fn as_sequence() -> &'static PySequenceMethods {
1730 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1731 length: atomic_func!(|seq, _vm| Ok(PyDictItems::sequence_downcast(seq).__len__())),
1732 contains: atomic_func!(|seq, target, vm| {
1733 let needle: &Py<PyTuple> = match target.downcast_ref() {
1734 Some(needle) => needle,
1735 None => return Ok(false),
1736 };
1737 if needle.as_slice().len() != 2 {
1738 return Ok(false);
1739 }
1740
1741 let zelf = PyDictItems::sequence_downcast(seq);
1742 let key = &needle.as_slice()[0];
1743 let Some(found) = zelf.dict().inner_getitem_opt(&**key, vm)? else {
1744 return Ok(false);
1745 };
1746 let value = &needle.as_slice()[1];
1747 vm.identical_or_equal(&found, value)
1748 }),
1749 ..PySequenceMethods::NOT_IMPLEMENTED
1750 });
1751 &AS_SEQUENCE
1752 }
1753}
1754
1755impl AsNumber for PyDictItems {
1756 fn as_number() -> &'static PyNumberMethods {
1757 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1758 subtract: Some(set_inner_number_subtract),
1759 and: Some(set_inner_number_and),
1760 xor: Some(dict_item_view_number_xor),
1761 or: Some(set_inner_number_or),
1762 ..PyNumberMethods::NOT_IMPLEMENTED
1763 };
1764 &AS_NUMBER
1765 }
1766}
1767
1768#[pyclass(
1769 flags(DISALLOW_INSTANTIATION),
1770 with(DictView, Iterable, AsSequence, Representable)
1771)]
1772impl PyDictValues {
1773 #[pygetset]
1774 fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
1775 PyMappingProxy::from(zelf.dict().to_owned())
1776 }
1777}
1778
1779impl AsSequence for PyDictValues {
1780 fn as_sequence() -> &'static PySequenceMethods {
1781 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1782 length: atomic_func!(|seq, _vm| Ok(PyDictValues::sequence_downcast(seq).__len__())),
1783 ..PySequenceMethods::NOT_IMPLEMENTED
1784 });
1785 &AS_SEQUENCE
1786 }
1787}
1788
1789fn set_inner_number_op<F>(a: &PyObject, b: &PyObject, f: F, vm: &VirtualMachine) -> PyResult
1790where
1791 F: FnOnce(PySetInner, ArgIterable) -> PyResult<PySetInner>,
1792{
1793 let a = PySetInner::from_iter(
1794 ArgIterable::try_from_object(vm, a.to_owned())?.iter(vm)?,
1795 vm,
1796 )?;
1797 let b = ArgIterable::try_from_object(vm, b.to_owned())?;
1798 Ok(PySet { inner: f(a, b)? }.into_pyobject(vm))
1799}
1800
1801pub(crate) fn set_inner_number_subtract(
1802 a: &PyObject,
1803 b: &PyObject,
1804 vm: &VirtualMachine,
1805) -> PyResult {
1806 set_inner_number_op(a, b, |a, b| a.difference(b, vm), vm)
1807}
1808
1809pub(crate) fn set_inner_number_and(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1810 let a_is_view =
1811 a.downcast_ref::<PyDictKeys>().is_some() || a.downcast_ref::<PyDictItems>().is_some();
1812 let (view, other) = if a_is_view { (a, b) } else { (b, a) };
1813 set_view_number_and(view, other, vm)
1814}
1815
1816pub(crate) fn set_view_number_and(
1817 view: &PyObject,
1818 other: &PyObject,
1819 vm: &VirtualMachine,
1820) -> PyResult {
1821 let other = ArgIterable::<PyObjectRef>::try_from_object(vm, other.to_owned())?;
1822 let result = PySet::default();
1823 for item in other.iter(vm)? {
1824 let item = item?;
1825 if view.sequence_unchecked().contains(&item, vm)? {
1826 result.add(item, vm)?;
1827 }
1828 }
1829 Ok(result.into_pyobject(vm))
1830}
1831
1832pub(crate) fn set_inner_number_xor(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1833 set_inner_number_op(a, b, |a, b| a.symmetric_difference(b, vm), vm)
1834}
1835
1836pub(crate) fn set_item_view_number_xor(
1837 a: &PyObject,
1838 b: &PyObject,
1839 both_are_item_views: bool,
1840 vm: &VirtualMachine,
1841) -> PyResult {
1842 if !both_are_item_views {
1843 return set_inner_number_xor(a, b, vm);
1844 }
1845 let result = PySet::default();
1846 let a_iterable = ArgIterable::<PyObjectRef>::try_from_object(vm, a.to_owned())?;
1847 for item in a_iterable.iter(vm)? {
1848 let item = item?;
1849 if !b.sequence_unchecked().contains(&item, vm)? {
1850 result.add(item, vm)?;
1851 }
1852 }
1853 let b_iterable = ArgIterable::<PyObjectRef>::try_from_object(vm, b.to_owned())?;
1854 for item in b_iterable.iter(vm)? {
1855 let item = item?;
1856 if !a.sequence_unchecked().contains(&item, vm)? {
1857 result.add(item, vm)?;
1858 }
1859 }
1860 Ok(result.into_pyobject(vm))
1861}
1862
1863fn dict_item_view_number_xor(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1864 let is_native_item_view = |obj: &PyObject| {
1865 obj.downcast_ref::<PyDictItems>().is_some()
1866 || obj.downcast_ref::<PyOrderedDictItems>().is_some()
1867 };
1868 set_item_view_number_xor(a, b, is_native_item_view(a) && is_native_item_view(b), vm)
1869}
1870
1871pub(crate) fn set_inner_number_or(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
1872 set_inner_number_op(a, b, |a, b| a.union(b, vm), vm)
1873}
1874
1875fn vectorcall_dict(
1876 zelf_obj: &PyObject,
1877 args: Vec<PyObjectRef>,
1878 nargs: usize,
1879 kwnames: Option<&[PyObjectRef]>,
1880 vm: &VirtualMachine,
1881) -> PyResult {
1882 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1883 let obj = PyDict::default().into_ref_with_type(vm, zelf.to_owned())?;
1884 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1885 PyDict::slot_init(obj.as_object(), func_args, vm)?;
1886 Ok(obj.into())
1887}
1888
1889pub(crate) fn init(context: &'static Context) {
1890 PyDict::extend_class(context, context.types.dict_type);
1891 context
1892 .types
1893 .dict_type
1894 .slots
1895 .vectorcall
1896 .store(Some(vectorcall_dict));
1897 PyDictKeys::extend_class(context, context.types.dict_keys_type);
1898 PyDictKeyIterator::extend_class(context, context.types.dict_keyiterator_type);
1899 PyDictReverseKeyIterator::extend_class(context, context.types.dict_reversekeyiterator_type);
1900 PyDictValues::extend_class(context, context.types.dict_values_type);
1901 PyDictValueIterator::extend_class(context, context.types.dict_valueiterator_type);
1902 PyDictReverseValueIterator::extend_class(context, context.types.dict_reversevalueiterator_type);
1903 PyDictItems::extend_class(context, context.types.dict_items_type);
1904 PyDictItemIterator::extend_class(context, context.types.dict_itemiterator_type);
1905 PyDictReverseItemIterator::extend_class(context, context.types.dict_reverseitemiterator_type);
1906}