@@ -36,49 +36,9 @@ impl crate::impl_::pyclass::PyClassBaseType for PyList {
3636 crate :: impl_:: pycell:: PyVariableClassObject < T > ;
3737}
3838
39- #[ inline]
40- #[ track_caller]
41- fn try_new_from_iter < ' py > (
42- py : Python < ' py > ,
43- mut elements : impl ExactSizeIterator < Item = PyResult < Bound < ' py , PyAny > > > ,
44- ) -> PyResult < Bound < ' py , PyList > > {
45- unsafe {
46- // PyList_New checks for overflow but has a bad error message, so we check ourselves
47- let len: Py_ssize_t = elements
48- . len ( )
49- . try_into ( )
50- . expect ( "out of range integral type conversion attempted on `elements.len()`" ) ;
51-
52- let ptr = ffi:: PyList_New ( len) ;
53-
54- // We create the `Bound` pointer here for two reasons:
55- // - panics if the ptr is null
56- // - its Drop cleans up the list if user code or the asserts panic.
57- let list = ptr. assume_owned ( py) . cast_into_unchecked ( ) ;
58-
59- let count = ( & mut elements)
60- . take ( len as usize )
61- . try_fold ( 0 , |count, item| {
62- #[ cfg( not( Py_LIMITED_API ) ) ]
63- ffi:: PyList_SET_ITEM ( ptr, count, item?. into_ptr ( ) ) ;
64- #[ cfg( Py_LIMITED_API ) ]
65- ffi:: PyList_SetItem ( ptr, count, item?. into_ptr ( ) ) ;
66- Ok :: < _ , PyErr > ( count + 1 )
67- } ) ?;
68-
69- assert ! ( elements. next( ) . is_none( ) , "Attempted to create PyList but `elements` was larger than reported by its `ExactSizeIterator` implementation." ) ;
70- assert_eq ! ( len, count, "Attempted to create PyList but `elements` was smaller than reported by its `ExactSizeIterator` implementation." ) ;
71-
72- Ok ( list)
73- }
74- }
75-
7639impl PyList {
7740 /// Constructs a new list with the given elements.
7841 ///
79- /// If you want to create a [`PyList`] with elements of different or unknown types, or from an
80- /// iterable that doesn't implement [`ExactSizeIterator`], use [`PyListMethods::append`].
81- ///
8242 /// # Examples
8343 ///
8444 /// ```rust
@@ -97,20 +57,42 @@ impl PyList {
9757 ///
9858 /// # Panics
9959 ///
100- /// This function will panic if `element`'s [`ExactSizeIterator `] implementation is incorrect.
60+ /// This function will panic if `element`'s [`Iterator::size_hint `] implementation is incorrect.
10161 /// All standard library structures implement this trait correctly, if they do, so calling this
10262 /// function with (for example) [`Vec`]`<T>` or `&[T]` will always succeed.
10363 #[ track_caller]
104- pub fn new < ' py , T , U > (
64+ pub fn new < ' py , T > (
10565 py : Python < ' py > ,
106- elements : impl IntoIterator < Item = T , IntoIter = U > ,
66+ elements : impl IntoIterator < Item = T > ,
10767 ) -> PyResult < Bound < ' py , PyList > >
10868 where
10969 T : IntoPyObject < ' py > ,
110- U : ExactSizeIterator < Item = T > ,
11170 {
112- let iter = elements. into_iter ( ) . map ( |e| e. into_bound_py_any ( py) ) ;
113- try_new_from_iter ( py, iter)
71+ let mut elements = elements. into_iter ( ) . map ( |e| e. into_bound_py_any ( py) ) ;
72+ let ( min_len, _) = elements. size_hint ( ) ;
73+
74+ // PyList_New checks for overflow but has a bad error message, so we check ourselves
75+ let len: Py_ssize_t = min_len
76+ . try_into ( )
77+ . expect ( "out of range integral type conversion attempted on `elements.len()`" ) ;
78+
79+ let list = unsafe { ffi:: PyList_New ( len) . assume_owned ( py) . cast_into_unchecked ( ) } ;
80+
81+ let count = ( & mut elements)
82+ . take ( len as usize )
83+ . try_fold ( 0 , |count, item| unsafe {
84+ #[ cfg( not( Py_LIMITED_API ) ) ]
85+ ffi:: PyList_SET_ITEM ( list. as_ptr ( ) , count, item?. into_ptr ( ) ) ;
86+ #[ cfg( Py_LIMITED_API ) ]
87+ ffi:: PyList_SetItem ( list. as_ptr ( ) , count, item?. into_ptr ( ) ) ;
88+ Ok :: < _ , PyErr > ( count + 1 )
89+ } ) ?;
90+
91+ assert_eq ! ( len, count, "Attempted to create PyList but `elements` was smaller than reported by its `size_hint` implementation." ) ;
92+
93+ elements. try_for_each ( |item| list. append ( item?) ) ?;
94+
95+ Ok ( list)
11496 }
11597
11698 /// Constructs a new empty list.
@@ -1506,7 +1488,7 @@ mod tests {
15061488
15071489 use std:: ops:: Range ;
15081490
1509- // An iterator that lies about its `ExactSizeIterator ` implementation.
1491+ // An iterator that lies about its `size_hint ` implementation.
15101492 // See https://github.com/PyO3/pyo3/issues/2118
15111493 struct FaultyIter ( Range < usize > , usize ) ;
15121494
@@ -1516,28 +1498,15 @@ mod tests {
15161498 fn next ( & mut self ) -> Option < Self :: Item > {
15171499 self . 0 . next ( )
15181500 }
1519- }
15201501
1521- impl ExactSizeIterator for FaultyIter {
1522- fn len ( & self ) -> usize {
1523- self . 1
1502+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
1503+ ( self . 1 , Some ( self . 1 ) )
15241504 }
15251505 }
15261506
15271507 #[ test]
15281508 #[ should_panic(
1529- expected = "Attempted to create PyList but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1530- ) ]
1531- fn too_long_iterator ( ) {
1532- Python :: attach ( |py| {
1533- let iter = FaultyIter ( 0 ..usize:: MAX , 73 ) ;
1534- let _list = PyList :: new ( py, iter) . unwrap ( ) ;
1535- } )
1536- }
1537-
1538- #[ test]
1539- #[ should_panic(
1540- expected = "Attempted to create PyList but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1509+ expected = "Attempted to create PyList but `elements` was smaller than reported by its `size_hint` implementation."
15411510 ) ]
15421511 fn too_short_iterator ( ) {
15431512 Python :: attach ( |py| {
@@ -1597,11 +1566,9 @@ mod tests {
15971566 Bad ( i)
15981567 } )
15991568 }
1600- }
16011569
1602- impl ExactSizeIterator for FaultyIter {
1603- fn len ( & self ) -> usize {
1604- self . 1
1570+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
1571+ ( self . 1 , Some ( self . 1 ) )
16051572 }
16061573 }
16071574
@@ -1798,4 +1765,25 @@ mod tests {
17981765 assert_eq ! ( list. iter( ) . count( ) , 3 ) ;
17991766 } )
18001767 }
1768+
1769+ #[ test]
1770+ fn test_new_from_non_exact_iter ( ) {
1771+ Python :: attach ( |py| {
1772+ let iter = ( 0 ..5 )
1773+ . filter ( |_| true ) // Filter does not implement ExactSizeIterator
1774+ . map ( |item| item. into_pyobject ( py) . unwrap ( ) ) ;
1775+
1776+ assert ! (
1777+ matches!( iter. size_hint( ) , ( 0 , _) ) ,
1778+ "size_hint lower bound should be 0 because we do not now the final size after filter"
1779+ ) ;
1780+
1781+ let list = PyList :: new ( py, iter) . unwrap ( ) ;
1782+ assert_eq ! (
1783+ list. len( ) ,
1784+ 5 ,
1785+ "list should contain all elements even though size_hint is 0"
1786+ ) ;
1787+ } )
1788+ }
18011789}
0 commit comments