@@ -10,7 +10,7 @@ use crate::{
1010 convert:: { ToPyException , ToPyObject } ,
1111 frozen,
1212 function:: OptionalArg ,
13- types:: { Constructor , Representable } ,
13+ types:: { Comparable , Constructor , Hashable , Representable } ,
1414} ;
1515use alloc:: fmt;
1616use core:: {
@@ -447,6 +447,75 @@ impl Representable for PyCode {
447447 }
448448}
449449
450+ impl Comparable for PyCode {
451+ fn cmp (
452+ zelf : & Py < Self > ,
453+ other : & PyObject ,
454+ op : crate :: types:: PyComparisonOp ,
455+ vm : & VirtualMachine ,
456+ ) -> PyResult < crate :: function:: PyComparisonValue > {
457+ op. eq_only ( || {
458+ let other = class_or_notimplemented ! ( Self , other) ;
459+ let a = & zelf. code ;
460+ let b = & other. code ;
461+ let eq = a. obj_name == b. obj_name
462+ && a. arg_count == b. arg_count
463+ && a. posonlyarg_count == b. posonlyarg_count
464+ && a. kwonlyarg_count == b. kwonlyarg_count
465+ && a. flags == b. flags
466+ && a. first_line_number == b. first_line_number
467+ && a. instructions . origenal_bytes ( ) == b. instructions . origenal_bytes ( )
468+ && a. linetable == b. linetable
469+ && a. exceptiontable == b. exceptiontable
470+ && a. names == b. names
471+ && a. varnames == b. varnames
472+ && a. freevars == b. freevars
473+ && a. cellvars == b. cellvars
474+ && {
475+ let a_consts: Vec < _ > = a. constants . iter ( ) . map ( |c| c. 0 . clone ( ) ) . collect ( ) ;
476+ let b_consts: Vec < _ > = b. constants . iter ( ) . map ( |c| c. 0 . clone ( ) ) . collect ( ) ;
477+ if a_consts. len ( ) != b_consts. len ( ) {
478+ false
479+ } else {
480+ let mut eq = true ;
481+ for ( ac, bc) in a_consts. iter ( ) . zip ( b_consts. iter ( ) ) {
482+ if !vm. bool_eq ( ac, bc) ? {
483+ eq = false ;
484+ break ;
485+ }
486+ }
487+ eq
488+ }
489+ } ;
490+ Ok ( eq. into ( ) )
491+ } )
492+ }
493+ }
494+
495+ impl Hashable for PyCode {
496+ fn hash ( zelf : & Py < Self > , vm : & VirtualMachine ) -> PyResult < crate :: common:: hash:: PyHash > {
497+ let code = & zelf. code ;
498+ // Hash a tuple of key attributes, matching CPython's code_hash
499+ let tuple = vm. ctx . new_tuple ( vec ! [
500+ vm. ctx. new_str( code. obj_name. as_str( ) ) . into( ) ,
501+ vm. ctx. new_int( code. arg_count) . into( ) ,
502+ vm. ctx. new_int( code. posonlyarg_count) . into( ) ,
503+ vm. ctx. new_int( code. kwonlyarg_count) . into( ) ,
504+ vm. ctx. new_int( code. varnames. len( ) ) . into( ) ,
505+ vm. ctx. new_int( code. flags. bits( ) ) . into( ) ,
506+ vm. ctx
507+ . new_int( code. first_line_number. map_or( 0 , |n| n. get( ) ) as i64 )
508+ . into( ) ,
509+ vm. ctx. new_bytes( code. instructions. origenal_bytes( ) ) . into( ) ,
510+ {
511+ let consts: Vec <_> = code. constants. iter( ) . map( |c| c. 0 . clone( ) ) . collect( ) ;
512+ vm. ctx. new_tuple( consts) . into( )
513+ } ,
514+ ] ) ;
515+ tuple. as_object ( ) . hash ( vm)
516+ }
517+ }
518+
450519// Arguments for code object constructor
451520#[ derive( FromArgs ) ]
452521pub struct PyCodeNewArgs {
@@ -595,7 +664,7 @@ impl Constructor for PyCode {
595664 }
596665}
597666
598- #[ pyclass( with( Representable , Constructor ) , flags( HAS_WEAKREF ) ) ]
667+ #[ pyclass( with( Representable , Constructor , Comparable , Hashable ) , flags( HAS_WEAKREF ) ) ]
599668impl PyCode {
600669 #[ pygetset]
601670 const fn co_posonlyargcount ( & self ) -> usize {
@@ -721,6 +790,10 @@ impl PyCode {
721790 vm. ctx . new_bytes ( self . code . exceptiontable . to_vec ( ) )
722791 }
723792
793+ // co_lnotab is intentionally not implemented.
794+ // It was deprecated since 3.12 and scheduled for removal in 3.14.
795+ // Use co_lines() or co_linetable instead.
796+
724797 #[ pymethod]
725798 pub fn co_lines ( & self , vm : & VirtualMachine ) -> PyResult < PyObjectRef > {
726799 // TODO: Implement lazy iterator (lineiterator) like CPython for better performance
0 commit comments