@@ -224,12 +224,11 @@ impl BuildSet for &PySet {
224
224
225
225
impl BuildSet for & PyFrozenSet {
226
226
fn build_add ( & self , item : PyObject ) -> PyResult < ( ) > {
227
- unsafe {
228
- py_error_on_minusone (
229
- self . py ( ) ,
230
- ffi:: PySet_Add ( self . as_ptr ( ) , item. to_object ( self . py ( ) ) . as_ptr ( ) ) ,
231
- )
232
- }
227
+ py_error_on_minusone ( self . py ( ) , unsafe {
228
+ // Safety: self.as_ptr() the _only_ pointer to the `frozenset`, and it's allowed
229
+ // to mutate this via the C API when nothing else can refer to it.
230
+ ffi:: PySet_Add ( self . as_ptr ( ) , item. to_object ( self . py ( ) ) . as_ptr ( ) )
231
+ } )
233
232
}
234
233
235
234
fn build_len ( & self ) -> usize {
@@ -492,57 +491,32 @@ impl<'py> Iterator for MappingGenericIterator<'py> {
492
491
type Item = ValResult < ' py , ( & ' py PyAny , & ' py PyAny ) > ;
493
492
494
493
fn next ( & mut self ) -> Option < Self :: Item > {
495
- let item = match self . iter . next ( ) {
496
- Some ( Err ( e) ) => return Some ( Err ( mapping_err ( e, self . iter . py ( ) , self . input ) ) ) ,
497
- Some ( Ok ( item) ) => item,
498
- None => return None ,
499
- } ;
500
- let tuple: & PyTuple = match item. downcast ( ) {
501
- Ok ( tuple) => tuple,
502
- Err ( _) => {
503
- return Some ( Err ( ValError :: new (
494
+ Some ( match self . iter . next ( ) ? {
495
+ Ok ( item) => item. extract ( ) . map_err ( |_| {
496
+ ValError :: new (
504
497
ErrorType :: MappingType {
505
498
error : MAPPING_TUPLE_ERROR . into ( ) ,
506
499
context : None ,
507
500
} ,
508
501
self . input ,
509
- ) ) )
510
- }
511
- } ;
512
- if tuple. len ( ) != 2 {
513
- return Some ( Err ( ValError :: new (
514
- ErrorType :: MappingType {
515
- error : MAPPING_TUPLE_ERROR . into ( ) ,
516
- context : None ,
517
- } ,
518
- self . input ,
519
- ) ) ) ;
520
- } ;
521
- #[ cfg( PyPy ) ]
522
- let key = tuple. get_item ( 0 ) . unwrap ( ) ;
523
- #[ cfg( PyPy ) ]
524
- let value = tuple. get_item ( 1 ) . unwrap ( ) ;
525
- #[ cfg( not( PyPy ) ) ]
526
- let key = unsafe { tuple. get_item_unchecked ( 0 ) } ;
527
- #[ cfg( not( PyPy ) ) ]
528
- let value = unsafe { tuple. get_item_unchecked ( 1 ) } ;
529
- Some ( Ok ( ( key, value) ) )
502
+ )
503
+ } ) ,
504
+ Err ( e) => Err ( mapping_err ( e, self . iter . py ( ) , self . input ) ) ,
505
+ } )
530
506
}
531
- // size_hint is omitted as it isn't needed
532
507
}
533
508
534
509
pub struct AttributesGenericIterator < ' py > {
535
510
object : & ' py PyAny ,
536
- attributes : & ' py PyList ,
537
- index : usize ,
511
+ // PyO3 should export this type upstream
512
+ attributes_iterator : < & ' py PyList as IntoIterator > :: IntoIter ,
538
513
}
539
514
540
515
impl < ' py > AttributesGenericIterator < ' py > {
541
516
pub fn new ( py_any : & ' py PyAny ) -> ValResult < ' py , Self > {
542
517
Ok ( Self {
543
518
object : py_any,
544
- attributes : py_any. dir ( ) ,
545
- index : 0 ,
519
+ attributes_iterator : py_any. dir ( ) . into_iter ( ) ,
546
520
} )
547
521
}
548
522
}
@@ -553,37 +527,31 @@ impl<'py> Iterator for AttributesGenericIterator<'py> {
553
527
fn next ( & mut self ) -> Option < Self :: Item > {
554
528
// loop until we find an attribute who's name does not start with underscore,
555
529
// or we get to the end of the list of attributes
556
- while self . index < self . attributes . len ( ) {
557
- #[ cfg( PyPy ) ]
558
- let name: & PyAny = self . attributes . get_item ( self . index ) . unwrap ( ) ;
559
- #[ cfg( not( PyPy ) ) ]
560
- let name: & PyAny = unsafe { self . attributes . get_item_unchecked ( self . index ) } ;
561
- self . index += 1 ;
562
- // from benchmarks this is 14x faster than using the python `startswith` method
563
- let name_cow = match name. downcast :: < PyString > ( ) {
564
- Ok ( name) => name. to_string_lossy ( ) ,
565
- Err ( e) => return Some ( Err ( e. into ( ) ) ) ,
566
- } ;
567
- if !name_cow. as_ref ( ) . starts_with ( '_' ) {
568
- // getattr is most likely to fail due to an exception in a @property, skip
569
- if let Ok ( attr) = self . object . getattr ( name_cow. as_ref ( ) ) {
570
- // we don't want bound methods to be included, is there a better way to check?
571
- // ref https://stackoverflow.com/a/18955425/949890
572
- let is_bound = matches ! ( attr. hasattr( intern!( attr. py( ) , "__self__" ) ) , Ok ( true ) ) ;
573
- // the PyFunction::is_type_of(attr) catches `staticmethod`, but also any other function,
574
- // I think that's better than including static methods in the yielded attributes,
575
- // if someone really wants fields, they can use an explicit field, or a function to modify input
576
- #[ cfg( not( PyPy ) ) ]
577
- if !is_bound && !PyFunction :: is_type_of ( attr) {
578
- return Some ( Ok ( ( name, attr) ) ) ;
579
- }
580
- // MASSIVE HACK! PyFunction doesn't exist for PyPy,
581
- // is_instance_of::<PyFunction> crashes with a null pointer, hence this hack, see
582
- // https://github.com/pydantic/pydantic-core/pull/161#discussion_r917257635
583
- #[ cfg( PyPy ) ]
584
- if !is_bound && attr. get_type ( ) . to_string ( ) != "<class 'function'>" {
585
- return Some ( Ok ( ( name, attr) ) ) ;
586
- }
530
+ let name = self . attributes_iterator . next ( ) ?;
531
+ // from benchmarks this is 14x faster than using the python `startswith` method
532
+ let name_cow = match name. downcast :: < PyString > ( ) {
533
+ Ok ( name) => name. to_string_lossy ( ) ,
534
+ Err ( e) => return Some ( Err ( e. into ( ) ) ) ,
535
+ } ;
536
+ if !name_cow. as_ref ( ) . starts_with ( '_' ) {
537
+ // getattr is most likely to fail due to an exception in a @property, skip
538
+ if let Ok ( attr) = self . object . getattr ( name_cow. as_ref ( ) ) {
539
+ // we don't want bound methods to be included, is there a better way to check?
540
+ // ref https://stackoverflow.com/a/18955425/949890
541
+ let is_bound = matches ! ( attr. hasattr( intern!( attr. py( ) , "__self__" ) ) , Ok ( true ) ) ;
542
+ // the PyFunction::is_type_of(attr) catches `staticmethod`, but also any other function,
543
+ // I think that's better than including static methods in the yielded attributes,
544
+ // if someone really wants fields, they can use an explicit field, or a function to modify input
545
+ #[ cfg( not( PyPy ) ) ]
546
+ if !is_bound && !PyFunction :: is_type_of ( attr) {
547
+ return Some ( Ok ( ( name, attr) ) ) ;
548
+ }
549
+ // MASSIVE HACK! PyFunction doesn't exist for PyPy,
550
+ // is_instance_of::<PyFunction> crashes with a null pointer, hence this hack, see
551
+ // https://github.com/pydantic/pydantic-core/pull/161#discussion_r917257635
552
+ #[ cfg( PyPy ) ]
553
+ if !is_bound && attr. get_type ( ) . to_string ( ) != "<class 'function'>" {
554
+ return Some ( Ok ( ( name, attr) ) ) ;
587
555
}
588
556
}
589
557
}
@@ -621,12 +589,7 @@ pub enum GenericIterator {
621
589
622
590
impl From < JsonArray > for GenericIterator {
623
591
fn from ( array : JsonArray ) -> Self {
624
- let length = array. len ( ) ;
625
- let json_iter = GenericJsonIterator {
626
- array,
627
- length,
628
- index : 0 ,
629
- } ;
592
+ let json_iter = GenericJsonIterator { array, index : 0 } ;
630
593
Self :: JsonArray ( json_iter)
631
594
}
632
595
}
@@ -674,14 +637,15 @@ impl GenericPyIterator {
674
637
#[ derive( Debug , Clone ) ]
675
638
pub struct GenericJsonIterator {
676
639
array : JsonArray ,
677
- length : usize ,
678
640
index : usize ,
679
641
}
680
642
681
643
impl GenericJsonIterator {
682
644
pub fn next ( & mut self , _py : Python ) -> PyResult < Option < ( & JsonInput , usize ) > > {
683
- if self . index < self . length {
684
- let next = unsafe { self . array . get_unchecked ( self . index ) } ;
645
+ if self . index < self . array . len ( ) {
646
+ // panic here is impossible due to bounds check above; compiler should be
647
+ // able to optimize it away even
648
+ let next = & self . array [ self . index ] ;
685
649
let a = ( next, self . index ) ;
686
650
self . index += 1 ;
687
651
Ok ( Some ( a) )
@@ -940,13 +904,7 @@ impl<'a> EitherFloat<'a> {
940
904
pub fn as_f64 ( self ) -> f64 {
941
905
match self {
942
906
EitherFloat :: F64 ( f) => f,
943
-
944
- EitherFloat :: Py ( f) => {
945
- {
946
- // Safety: known to be a python float
947
- unsafe { ffi:: PyFloat_AS_DOUBLE ( f. as_ptr ( ) ) }
948
- }
949
- }
907
+ EitherFloat :: Py ( f) => f. value ( ) ,
950
908
}
951
909
}
952
910
}
0 commit comments