| 1 | // Copyright (c) 2017-present PyO3 Project and Contributors |
| 2 | |
| 3 | use super::PyMapping; |
| 4 | use crate::err::PyResult; |
| 5 | use crate::ffi_ptr_ext::FfiPtrExt; |
| 6 | use crate::instance::Bound; |
| 7 | use crate::types::any::PyAnyMethods; |
| 8 | use crate::types::{PyAny, PyIterator, PyList}; |
| 9 | use crate::{ffi, Python}; |
| 10 | |
| 11 | use std::os::raw::c_int; |
| 12 | |
| 13 | /// Represents a Python `mappingproxy`. |
| 14 | #[repr (transparent)] |
| 15 | pub struct PyMappingProxy(PyAny); |
| 16 | |
| 17 | #[inline ] |
| 18 | unsafe fn dict_proxy_check(op: *mut ffi::PyObject) -> c_int { |
| 19 | unsafe { ffi::Py_IS_TYPE(ob:op, tp:std::ptr::addr_of_mut!(ffi::PyDictProxy_Type)) } |
| 20 | } |
| 21 | |
| 22 | pyobject_native_type_core!( |
| 23 | PyMappingProxy, |
| 24 | pyobject_native_static_type_object!(ffi::PyDictProxy_Type), |
| 25 | #checkfunction=dict_proxy_check |
| 26 | ); |
| 27 | |
| 28 | impl PyMappingProxy { |
| 29 | /// Creates a mappingproxy from an object. |
| 30 | pub fn new<'py>( |
| 31 | py: Python<'py>, |
| 32 | elements: &Bound<'py, PyMapping>, |
| 33 | ) -> Bound<'py, PyMappingProxy> { |
| 34 | unsafe { |
| 35 | ffiBound<'_, PyAny>::PyDictProxy_New(arg1:elements.as_ptr()) |
| 36 | .assume_owned(py) |
| 37 | .downcast_into_unchecked() |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | /// Implementation of functionality for [`PyMappingProxy`]. |
| 43 | /// |
| 44 | /// These methods are defined for the `Bound<'py, PyMappingProxy>` smart pointer, so to use method call |
| 45 | /// syntax these methods are separated into a trait, because stable Rust does not yet support |
| 46 | /// `arbitrary_self_types`. |
| 47 | #[doc (alias = "PyMappingProxy" )] |
| 48 | pub trait PyMappingProxyMethods<'py, 'a>: crate::sealed::Sealed { |
| 49 | /// Checks if the mappingproxy is empty, i.e. `len(self) == 0`. |
| 50 | fn is_empty(&self) -> PyResult<bool>; |
| 51 | |
| 52 | /// Returns a list containing all keys in the mapping. |
| 53 | fn keys(&self) -> PyResult<Bound<'py, PyList>>; |
| 54 | |
| 55 | /// Returns a list containing all values in the mapping. |
| 56 | fn values(&self) -> PyResult<Bound<'py, PyList>>; |
| 57 | |
| 58 | /// Returns a list of tuples of all (key, value) pairs in the mapping. |
| 59 | fn items(&self) -> PyResult<Bound<'py, PyList>>; |
| 60 | |
| 61 | /// Returns `self` cast as a `PyMapping`. |
| 62 | fn as_mapping(&self) -> &Bound<'py, PyMapping>; |
| 63 | |
| 64 | /// Takes an object and returns an iterator for it. Returns an error if the object is not |
| 65 | /// iterable. |
| 66 | fn try_iter(&'a self) -> PyResult<BoundMappingProxyIterator<'py, 'a>>; |
| 67 | } |
| 68 | |
| 69 | impl<'py, 'a> PyMappingProxyMethods<'py, 'a> for Bound<'py, PyMappingProxy> { |
| 70 | fn is_empty(&self) -> PyResult<bool> { |
| 71 | Ok(self.len()? == 0) |
| 72 | } |
| 73 | |
| 74 | #[inline ] |
| 75 | fn keys(&self) -> PyResult<Bound<'py, PyList>> { |
| 76 | unsafe { |
| 77 | Ok(ffi::PyMapping_Keys(self.as_ptr()) |
| 78 | .assume_owned_or_err(self.py())? |
| 79 | .downcast_into_unchecked()) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | #[inline ] |
| 84 | fn values(&self) -> PyResult<Bound<'py, PyList>> { |
| 85 | unsafe { |
| 86 | Ok(ffi::PyMapping_Values(self.as_ptr()) |
| 87 | .assume_owned_or_err(self.py())? |
| 88 | .downcast_into_unchecked()) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | #[inline ] |
| 93 | fn items(&self) -> PyResult<Bound<'py, PyList>> { |
| 94 | unsafe { |
| 95 | Ok(ffi::PyMapping_Items(self.as_ptr()) |
| 96 | .assume_owned_or_err(self.py())? |
| 97 | .downcast_into_unchecked()) |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | fn as_mapping(&self) -> &Bound<'py, PyMapping> { |
| 102 | unsafe { self.downcast_unchecked() } |
| 103 | } |
| 104 | |
| 105 | fn try_iter(&'a self) -> PyResult<BoundMappingProxyIterator<'py, 'a>> { |
| 106 | Ok(BoundMappingProxyIterator { |
| 107 | iterator: PyIterator::from_object(self)?, |
| 108 | mappingproxy: self, |
| 109 | }) |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | pub struct BoundMappingProxyIterator<'py, 'a> { |
| 114 | iterator: Bound<'py, PyIterator>, |
| 115 | mappingproxy: &'a Bound<'py, PyMappingProxy>, |
| 116 | } |
| 117 | |
| 118 | impl<'py> Iterator for BoundMappingProxyIterator<'py, '_> { |
| 119 | type Item = PyResult<(Bound<'py, PyAny>, Bound<'py, PyAny>)>; |
| 120 | |
| 121 | #[inline ] |
| 122 | fn next(&mut self) -> Option<Self::Item> { |
| 123 | self.iterator.next().map(|key: Result, …>| match key { |
| 124 | Ok(key: Bound<'py, PyAny>) => match self.mappingproxy.get_item(&key) { |
| 125 | Ok(value: Bound<'py, PyAny>) => Ok((key, value)), |
| 126 | Err(e: PyErr) => Err(e), |
| 127 | }, |
| 128 | Err(e: PyErr) => Err(e), |
| 129 | }) |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | #[cfg (test)] |
| 134 | mod tests { |
| 135 | |
| 136 | use super::*; |
| 137 | use crate::types::dict::*; |
| 138 | use crate::Python; |
| 139 | use crate::{ |
| 140 | exceptions::PyKeyError, |
| 141 | types::{PyInt, PyTuple}, |
| 142 | }; |
| 143 | use std::collections::{BTreeMap, HashMap}; |
| 144 | |
| 145 | #[test ] |
| 146 | fn test_new() { |
| 147 | Python::with_gil(|py| { |
| 148 | let pydict = [(7, 32)].into_py_dict(py).unwrap(); |
| 149 | let mappingproxy = PyMappingProxy::new(py, pydict.as_mapping()); |
| 150 | mappingproxy.get_item(7i32).unwrap(); |
| 151 | assert_eq!( |
| 152 | 32, |
| 153 | mappingproxy |
| 154 | .get_item(7i32) |
| 155 | .unwrap() |
| 156 | .extract::<i32>() |
| 157 | .unwrap() |
| 158 | ); |
| 159 | assert!(mappingproxy |
| 160 | .get_item(8i32) |
| 161 | .unwrap_err() |
| 162 | .is_instance_of::<PyKeyError>(py)); |
| 163 | }); |
| 164 | } |
| 165 | |
| 166 | #[test ] |
| 167 | fn test_len() { |
| 168 | Python::with_gil(|py| { |
| 169 | let mut v = HashMap::new(); |
| 170 | let dict = v.clone().into_py_dict(py).unwrap(); |
| 171 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 172 | assert_eq!(mappingproxy.len().unwrap(), 0); |
| 173 | v.insert(7, 32); |
| 174 | let dict2 = v.clone().into_py_dict(py).unwrap(); |
| 175 | let mp2 = PyMappingProxy::new(py, dict2.as_mapping()); |
| 176 | assert_eq!(mp2.len().unwrap(), 1); |
| 177 | }); |
| 178 | } |
| 179 | |
| 180 | #[test ] |
| 181 | fn test_contains() { |
| 182 | Python::with_gil(|py| { |
| 183 | let mut v = HashMap::new(); |
| 184 | v.insert(7, 32); |
| 185 | let dict = v.clone().into_py_dict(py).unwrap(); |
| 186 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 187 | assert!(mappingproxy.contains(7i32).unwrap()); |
| 188 | assert!(!mappingproxy.contains(8i32).unwrap()); |
| 189 | }); |
| 190 | } |
| 191 | |
| 192 | #[test ] |
| 193 | fn test_get_item() { |
| 194 | Python::with_gil(|py| { |
| 195 | let mut v = HashMap::new(); |
| 196 | v.insert(7, 32); |
| 197 | let dict = v.clone().into_py_dict(py).unwrap(); |
| 198 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 199 | assert_eq!( |
| 200 | 32, |
| 201 | mappingproxy |
| 202 | .get_item(7i32) |
| 203 | .unwrap() |
| 204 | .extract::<i32>() |
| 205 | .unwrap() |
| 206 | ); |
| 207 | assert!(mappingproxy |
| 208 | .get_item(8i32) |
| 209 | .unwrap_err() |
| 210 | .is_instance_of::<PyKeyError>(py)); |
| 211 | }); |
| 212 | } |
| 213 | |
| 214 | #[test ] |
| 215 | fn test_set_item_refcnt() { |
| 216 | Python::with_gil(|py| { |
| 217 | let cnt; |
| 218 | { |
| 219 | let none = py.None(); |
| 220 | cnt = none.get_refcnt(py); |
| 221 | let dict = [(10, none)].into_py_dict(py).unwrap(); |
| 222 | let _mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 223 | } |
| 224 | { |
| 225 | assert_eq!(cnt, py.None().get_refcnt(py)); |
| 226 | } |
| 227 | }); |
| 228 | } |
| 229 | |
| 230 | #[test ] |
| 231 | fn test_isempty() { |
| 232 | Python::with_gil(|py| { |
| 233 | let map: HashMap<usize, usize> = HashMap::new(); |
| 234 | let dict = map.into_py_dict(py).unwrap(); |
| 235 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 236 | assert!(mappingproxy.is_empty().unwrap()); |
| 237 | }); |
| 238 | } |
| 239 | |
| 240 | #[test ] |
| 241 | fn test_keys() { |
| 242 | Python::with_gil(|py| { |
| 243 | let mut v = HashMap::new(); |
| 244 | v.insert(7, 32); |
| 245 | v.insert(8, 42); |
| 246 | v.insert(9, 123); |
| 247 | let dict = v.into_py_dict(py).unwrap(); |
| 248 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 249 | // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. |
| 250 | let mut key_sum = 0; |
| 251 | for el in mappingproxy.keys().unwrap().try_iter().unwrap() { |
| 252 | key_sum += el.unwrap().extract::<i32>().unwrap(); |
| 253 | } |
| 254 | assert_eq!(7 + 8 + 9, key_sum); |
| 255 | }); |
| 256 | } |
| 257 | |
| 258 | #[test ] |
| 259 | fn test_values() { |
| 260 | Python::with_gil(|py| { |
| 261 | let mut v: HashMap<i32, i32> = HashMap::new(); |
| 262 | v.insert(7, 32); |
| 263 | v.insert(8, 42); |
| 264 | v.insert(9, 123); |
| 265 | let dict = v.into_py_dict(py).unwrap(); |
| 266 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 267 | // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. |
| 268 | let mut values_sum = 0; |
| 269 | for el in mappingproxy.values().unwrap().try_iter().unwrap() { |
| 270 | values_sum += el.unwrap().extract::<i32>().unwrap(); |
| 271 | } |
| 272 | assert_eq!(32 + 42 + 123, values_sum); |
| 273 | }); |
| 274 | } |
| 275 | |
| 276 | #[test ] |
| 277 | fn test_items() { |
| 278 | Python::with_gil(|py| { |
| 279 | let mut v = HashMap::new(); |
| 280 | v.insert(7, 32); |
| 281 | v.insert(8, 42); |
| 282 | v.insert(9, 123); |
| 283 | let dict = v.into_py_dict(py).unwrap(); |
| 284 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 285 | // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. |
| 286 | let mut key_sum = 0; |
| 287 | let mut value_sum = 0; |
| 288 | for res in mappingproxy.items().unwrap().try_iter().unwrap() { |
| 289 | let el = res.unwrap(); |
| 290 | let tuple = el.downcast::<PyTuple>().unwrap(); |
| 291 | key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap(); |
| 292 | value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap(); |
| 293 | } |
| 294 | assert_eq!(7 + 8 + 9, key_sum); |
| 295 | assert_eq!(32 + 42 + 123, value_sum); |
| 296 | }); |
| 297 | } |
| 298 | |
| 299 | #[test ] |
| 300 | fn test_iter() { |
| 301 | Python::with_gil(|py| { |
| 302 | let mut v = HashMap::new(); |
| 303 | v.insert(7, 32); |
| 304 | v.insert(8, 42); |
| 305 | v.insert(9, 123); |
| 306 | let dict = v.into_py_dict(py).unwrap(); |
| 307 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 308 | let mut key_sum = 0; |
| 309 | let mut value_sum = 0; |
| 310 | for res in mappingproxy.try_iter().unwrap() { |
| 311 | let (key, value) = res.unwrap(); |
| 312 | key_sum += key.extract::<i32>().unwrap(); |
| 313 | value_sum += value.extract::<i32>().unwrap(); |
| 314 | } |
| 315 | assert_eq!(7 + 8 + 9, key_sum); |
| 316 | assert_eq!(32 + 42 + 123, value_sum); |
| 317 | }); |
| 318 | } |
| 319 | |
| 320 | #[test ] |
| 321 | fn test_hashmap_into_python() { |
| 322 | Python::with_gil(|py| { |
| 323 | let mut map = HashMap::<i32, i32>::new(); |
| 324 | map.insert(1, 1); |
| 325 | |
| 326 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 327 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 328 | |
| 329 | assert_eq!(py_map.len().unwrap(), 1); |
| 330 | assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1); |
| 331 | }); |
| 332 | } |
| 333 | |
| 334 | #[test ] |
| 335 | fn test_hashmap_into_mappingproxy() { |
| 336 | Python::with_gil(|py| { |
| 337 | let mut map = HashMap::<i32, i32>::new(); |
| 338 | map.insert(1, 1); |
| 339 | |
| 340 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 341 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 342 | |
| 343 | assert_eq!(py_map.len().unwrap(), 1); |
| 344 | assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1); |
| 345 | }); |
| 346 | } |
| 347 | |
| 348 | #[test ] |
| 349 | fn test_btreemap_into_py() { |
| 350 | Python::with_gil(|py| { |
| 351 | let mut map = BTreeMap::<i32, i32>::new(); |
| 352 | map.insert(1, 1); |
| 353 | |
| 354 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 355 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 356 | |
| 357 | assert_eq!(py_map.len().unwrap(), 1); |
| 358 | assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1); |
| 359 | }); |
| 360 | } |
| 361 | |
| 362 | #[test ] |
| 363 | fn test_btreemap_into_mappingproxy() { |
| 364 | Python::with_gil(|py| { |
| 365 | let mut map = BTreeMap::<i32, i32>::new(); |
| 366 | map.insert(1, 1); |
| 367 | |
| 368 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 369 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 370 | |
| 371 | assert_eq!(py_map.len().unwrap(), 1); |
| 372 | assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1); |
| 373 | }); |
| 374 | } |
| 375 | |
| 376 | #[test ] |
| 377 | fn test_vec_into_mappingproxy() { |
| 378 | Python::with_gil(|py| { |
| 379 | let vec = vec![("a" , 1), ("b" , 2), ("c" , 3)]; |
| 380 | let dict = vec.clone().into_py_dict(py).unwrap(); |
| 381 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 382 | |
| 383 | assert_eq!(py_map.len().unwrap(), 3); |
| 384 | assert_eq!(py_map.get_item("b" ).unwrap().extract::<i32>().unwrap(), 2); |
| 385 | }); |
| 386 | } |
| 387 | |
| 388 | #[test ] |
| 389 | fn test_slice_into_mappingproxy() { |
| 390 | Python::with_gil(|py| { |
| 391 | let arr = [("a" , 1), ("b" , 2), ("c" , 3)]; |
| 392 | |
| 393 | let dict = arr.into_py_dict(py).unwrap(); |
| 394 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 395 | |
| 396 | assert_eq!(py_map.len().unwrap(), 3); |
| 397 | assert_eq!(py_map.get_item("b" ).unwrap().extract::<i32>().unwrap(), 2); |
| 398 | }); |
| 399 | } |
| 400 | |
| 401 | #[test ] |
| 402 | fn mappingproxy_as_mapping() { |
| 403 | Python::with_gil(|py| { |
| 404 | let mut map = HashMap::<i32, i32>::new(); |
| 405 | map.insert(1, 1); |
| 406 | |
| 407 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 408 | let py_map = PyMappingProxy::new(py, dict.as_mapping()); |
| 409 | |
| 410 | assert_eq!(py_map.as_mapping().len().unwrap(), 1); |
| 411 | assert_eq!( |
| 412 | py_map |
| 413 | .as_mapping() |
| 414 | .get_item(1) |
| 415 | .unwrap() |
| 416 | .extract::<i32>() |
| 417 | .unwrap(), |
| 418 | 1 |
| 419 | ); |
| 420 | }); |
| 421 | } |
| 422 | |
| 423 | #[cfg (not(PyPy))] |
| 424 | fn abc_mappingproxy(py: Python<'_>) -> Bound<'_, PyMappingProxy> { |
| 425 | let mut map = HashMap::<&'static str, i32>::new(); |
| 426 | map.insert("a" , 1); |
| 427 | map.insert("b" , 2); |
| 428 | map.insert("c" , 3); |
| 429 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 430 | PyMappingProxy::new(py, dict.as_mapping()) |
| 431 | } |
| 432 | |
| 433 | #[test ] |
| 434 | #[cfg (not(PyPy))] |
| 435 | fn mappingproxy_keys_view() { |
| 436 | Python::with_gil(|py| { |
| 437 | let mappingproxy = abc_mappingproxy(py); |
| 438 | let keys = mappingproxy.call_method0("keys" ).unwrap(); |
| 439 | assert!(keys.is_instance(&py.get_type::<PyDictKeys>()).unwrap()); |
| 440 | }) |
| 441 | } |
| 442 | |
| 443 | #[test ] |
| 444 | #[cfg (not(PyPy))] |
| 445 | fn mappingproxy_values_view() { |
| 446 | Python::with_gil(|py| { |
| 447 | let mappingproxy = abc_mappingproxy(py); |
| 448 | let values = mappingproxy.call_method0("values" ).unwrap(); |
| 449 | assert!(values.is_instance(&py.get_type::<PyDictValues>()).unwrap()); |
| 450 | }) |
| 451 | } |
| 452 | |
| 453 | #[test ] |
| 454 | #[cfg (not(PyPy))] |
| 455 | fn mappingproxy_items_view() { |
| 456 | Python::with_gil(|py| { |
| 457 | let mappingproxy = abc_mappingproxy(py); |
| 458 | let items = mappingproxy.call_method0("items" ).unwrap(); |
| 459 | assert!(items.is_instance(&py.get_type::<PyDictItems>()).unwrap()); |
| 460 | }) |
| 461 | } |
| 462 | |
| 463 | #[test ] |
| 464 | fn get_value_from_mappingproxy_of_strings() { |
| 465 | Python::with_gil(|py: Python<'_>| { |
| 466 | let mut map = HashMap::new(); |
| 467 | map.insert("first key" .to_string(), "first value" .to_string()); |
| 468 | map.insert("second key" .to_string(), "second value" .to_string()); |
| 469 | map.insert("third key" .to_string(), "third value" .to_string()); |
| 470 | |
| 471 | let dict = map.clone().into_py_dict(py).unwrap(); |
| 472 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 473 | |
| 474 | assert_eq!( |
| 475 | map.into_iter().collect::<Vec<(String, String)>>(), |
| 476 | mappingproxy |
| 477 | .try_iter() |
| 478 | .unwrap() |
| 479 | .map(|object| { |
| 480 | let tuple = object.unwrap(); |
| 481 | ( |
| 482 | tuple.0.extract::<String>().unwrap(), |
| 483 | tuple.1.extract::<String>().unwrap(), |
| 484 | ) |
| 485 | }) |
| 486 | .collect::<Vec<(String, String)>>() |
| 487 | ); |
| 488 | }) |
| 489 | } |
| 490 | |
| 491 | #[test ] |
| 492 | fn get_value_from_mappingproxy_of_integers() { |
| 493 | Python::with_gil(|py: Python<'_>| { |
| 494 | const LEN: usize = 10_000; |
| 495 | let items: Vec<(usize, usize)> = (1..LEN).map(|i| (i, i - 1)).collect(); |
| 496 | |
| 497 | let dict = items.clone().into_py_dict(py).unwrap(); |
| 498 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 499 | |
| 500 | assert_eq!( |
| 501 | items, |
| 502 | mappingproxy |
| 503 | .clone() |
| 504 | .try_iter() |
| 505 | .unwrap() |
| 506 | .map(|object| { |
| 507 | let tuple = object.unwrap(); |
| 508 | ( |
| 509 | tuple |
| 510 | .0 |
| 511 | .downcast::<PyInt>() |
| 512 | .unwrap() |
| 513 | .extract::<usize>() |
| 514 | .unwrap(), |
| 515 | tuple |
| 516 | .1 |
| 517 | .downcast::<PyInt>() |
| 518 | .unwrap() |
| 519 | .extract::<usize>() |
| 520 | .unwrap(), |
| 521 | ) |
| 522 | }) |
| 523 | .collect::<Vec<(usize, usize)>>() |
| 524 | ); |
| 525 | for index in 1..LEN { |
| 526 | assert_eq!( |
| 527 | mappingproxy |
| 528 | .clone() |
| 529 | .get_item(index) |
| 530 | .unwrap() |
| 531 | .extract::<usize>() |
| 532 | .unwrap(), |
| 533 | index - 1 |
| 534 | ); |
| 535 | } |
| 536 | }) |
| 537 | } |
| 538 | |
| 539 | #[test ] |
| 540 | fn iter_mappingproxy_nosegv() { |
| 541 | Python::with_gil(|py| { |
| 542 | const LEN: usize = 1_000; |
| 543 | let items = (0..LEN as u64).map(|i| (i, i * 2)); |
| 544 | |
| 545 | let dict = items.clone().into_py_dict(py).unwrap(); |
| 546 | let mappingproxy = PyMappingProxy::new(py, dict.as_mapping()); |
| 547 | |
| 548 | let mut sum = 0; |
| 549 | for result in mappingproxy.try_iter().unwrap() { |
| 550 | let (k, _v) = result.unwrap(); |
| 551 | let i: u64 = k.extract().unwrap(); |
| 552 | sum += i; |
| 553 | } |
| 554 | assert_eq!(sum, 499_500); |
| 555 | }) |
| 556 | } |
| 557 | } |
| 558 | |