@@ -495,6 +495,24 @@ let ``test PydanticUser with byte and int16 types`` () =
495495 model.Int16Val |> equal - 32768 s
496496 model.UInt16Val |> equal 65535 us
497497
498+ // Test Pydantic field_validator with @classmethod
499+ [<Py.ClassAttributes( style= Py.ClassAttributeStyle.Attributes, init= false ) >]
500+ type PydanticUserWithValidator ( Name : string ) =
501+ inherit BaseModel()
502+ member val Name : string = Name with get, set
503+
504+ [<Py.Decorate( " pydantic.field_validator" , " 'Name'" ) >]
505+ [<Py.ClassMethod>]
506+ static member validate_name ( cls : obj , v : string ) : string =
507+ v.ToUpper()
508+
509+ [<Fact>]
510+ let ``test Pydantic field_validator with classmethod`` () =
511+ // Test that @field_validator and @classmethod decorators are applied correctly
512+ // The validator should transform the Name to uppercase
513+ let user = emitPyExpr< PydanticUserWithValidator> [] " PydanticUserWithValidator(Name='john')"
514+ user.Name |> equal " JOHN"
515+
498516[<Py.Decorate( " dataclasses.dataclass" ) >]
499517[<Py.ClassAttributes( style= Py.ClassAttributeStyle.Attributes, init= false ) >]
500518type DecoratedUser () =
@@ -590,6 +608,90 @@ let ``test PropertiesUserWithInit`` () =
590608 user.Email
|> equal
( Some
" [email protected] " ) 591609 user.Enabled |> equal true
592610
611+ // Test Py.Decorate on static methods
612+
613+ [<AttachMembers>]
614+ type ClassWithDecoratedStaticMethod () =
615+ member val Value : int = 0 with get, set
616+
617+ [<Py.Decorate( " functools.lru_cache" ) >]
618+ static member cached_function ( x : int ) : int =
619+ x * 2
620+
621+ [<Fact>]
622+ let ``test Py.Decorate on static method`` () =
623+ // Test that @lru_cache decorator is applied to static method
624+ let result1 = ClassWithDecoratedStaticMethod.cached_ function( 5 )
625+ let result2 = ClassWithDecoratedStaticMethod.cached_ function( 5 )
626+ result1 |> equal 10
627+ result2 |> equal 10
628+
629+ [<AttachMembers>]
630+ type ClassWithDecoratedStaticMethodWithParams () =
631+ [<Py.Decorate( " functools.lru_cache" , " maxsize=32" ) >]
632+ static member cached_with_params ( x : int ) : int =
633+ x * 3
634+
635+ [<Fact>]
636+ let ``test Py.Decorate on static method with parameters`` () =
637+ // Test that decorator with parameters is applied to static method
638+ let result = ClassWithDecoratedStaticMethodWithParams.cached_ with_ params( 4 )
639+ result |> equal 12
640+
641+ [<AttachMembers>]
642+ type ClassWithClassMethod () =
643+ member val Name : string = " " with get, set
644+
645+ // Note: With @classmethod, Python automatically passes `cls` as first argument
646+ // So from F# we only pass the remaining arguments
647+ [<Py.ClassMethod>]
648+ static member create_instance ( cls : obj , name : string ) : ClassWithClassMethod =
649+ let instance = ClassWithClassMethod()
650+ instance.Name <- name
651+ instance
652+
653+ [<Fact>]
654+ let ``test Py.ClassMethod attribute`` () =
655+ // Test that @classmethod decorator is applied instead of @staticmethod
656+ // Note: cls is automatically passed by Python, so we only pass the name argument
657+ // We use Emit to call the method without the cls argument from F#
658+ let instance = emitPyExpr< ClassWithClassMethod> [] " ClassWithClassMethod.create_instance('TestName')"
659+ instance.Name |> equal " TestName"
660+
661+
662+ [<AttachMembers>]
663+ type ClassWithMultipleDecoratedMethods () =
664+ [<Py.Decorate( " functools.lru_cache" , " maxsize=16" ) >]
665+ static member method_a ( x : int ) : int = x * 2
666+
667+ [<Py.Decorate( " functools.lru_cache" , " maxsize=32" ) >]
668+ static member method_b ( x : int ) : int = x * 3
669+
670+ [<Fact>]
671+ let ``test multiple static methods with decorators`` () =
672+ // Test that decorators work on multiple static methods in same class
673+ let resultA = ClassWithMultipleDecoratedMethods.method_ a( 5 )
674+ let resultB = ClassWithMultipleDecoratedMethods.method_ b( 5 )
675+ resultA |> equal 10
676+ resultB |> equal 15
677+
678+ [<AttachMembers>]
679+ type ClassWithDecoratedInstanceMethod () =
680+ member val CallCount : int = 0 with get, set
681+
682+ [<Py.Decorate( " functools.lru_cache" , " maxsize=16" ) >]
683+ member this.cached_instance_method ( x : int ) : int =
684+ x * 4
685+
686+ [<Fact>]
687+ let ``test Py.Decorate on instance method`` () =
688+ // Test that decorator works on instance methods too
689+ let obj = ClassWithDecoratedInstanceMethod()
690+ let result1 = obj.cached_ instance_ method( 3 )
691+ let result2 = obj.cached_ instance_ method( 3 )
692+ result1 |> equal 12
693+ result2 |> equal 12
694+
593695// Import fable_library version to verify we're using local build, not PyPI
594696let fableLibraryVersion : string = import " __version__" " fable_library._version"
595697
0 commit comments