Skip to content

Commit e2203af

Browse files
authored
[Python] Add pydantic tests (#4293)
1 parent a0f2106 commit e2203af

File tree

3 files changed

+83
-9
lines changed

3 files changed

+83
-9
lines changed

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* [Python] Add Pythonic import path syntax for relative imports (`.module`, `..parent`, `...grandparent`) (by @dbrattli)
1213
* [Python] Add `[<Py.DecorateTemplate>]` attribute for creating custom decorator attributes (by @dbrattli)
1314
* [Python] Add `[<Py.ClassAttributesTemplate>]` attribute for creating custom class attribute shortcuts (by @dbrattli)
1415
* [Python] Add `[<Py.DataClass>]` as a built-in shorthand for `[<Py.ClassAttributes(style = Attributes, init = false)>]` (by @dbrattli)
@@ -18,10 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1819
* [Python] Fix regression `[<Erase>]` on class types not preventing them from being emitted to Python (by @dbrattli)
1920
* [Python] Fix regression `%A` format specifier to output booleans as lowercase `true`/`false` (by @dbrattli)
2021

21-
### Added
22-
23-
* [Python] Add Pythonic import path syntax for relative imports (`.module`, `..parent`, `...grandparent`) (by @dbrattli)
24-
2522
### Changed
2623

2724
* [Python] `[<Py.Decorate>]` now emits decorator strings verbatim and adds `importFrom` parameter for explicit import control (by @dbrattli)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* [Python] Add Pythonic import path syntax for relative imports (`.module`, `..parent`, `...grandparent`) (by @dbrattli)
1213
* [Python] Add `[<Py.DecorateTemplate>]` attribute for creating custom decorator attributes (by @dbrattli)
1314
* [Python] Add `[<Py.ClassAttributesTemplate>]` attribute for creating custom class attribute shortcuts (by @dbrattli)
1415
* [Python] Add `[<Py.DataClass>]` as a built-in shorthand for `[<Py.ClassAttributes(style = Attributes, init = false)>]` (by @dbrattli)
1516

1617
### Fixed
1718

1819
* [Python] Fix regression `[<Erase>]` on class types not preventing them from being emitted to Python (by @dbrattli)
19-
2020
* [Python] Fix regression `%A` format specifier to output booleans as lowercase `true`/`false` (by @dbrattli)
2121

22-
### Added
23-
24-
* [Python] Add Pythonic import path syntax for relative imports (`.module`, `..parent`, `...grandparent`) (by @dbrattli)
25-
2622
### Changed
2723

2824
* [Python] `[<Py.Decorate>]` now emits decorator strings verbatim and adds `importFrom` parameter for explicit import control (by @dbrattli)

tests/Python/TestPyInterop.fs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,4 +821,85 @@ let ``test ClassAttributesTemplate for custom class attributes`` () =
821821
user.Username |> equal "testuser"
822822
user.Email |> equal "[email protected]"
823823

824+
// Test Pydantic serialization of FSharpArray
825+
// This tests that FSharpArray and its subclasses (GenericArray, Int32Array, etc.)
826+
// can be serialized by Pydantic when used in BaseModel classes
827+
828+
[<Py.ClassAttributes(style=Py.ClassAttributeStyle.Attributes, init=false)>]
829+
type PydanticUserWithArray(Name: string, Scores: int[]) =
830+
inherit BaseModel()
831+
member val Name: string = Name with get, set
832+
member val Scores: int[] = Scores with get, set
833+
834+
[<Fact>]
835+
let ``test Pydantic serialization of FSharpArray`` () =
836+
// Test that FSharpArray can be used in a Pydantic model and serialized
837+
let user = PydanticUserWithArray(Name = "Test User", Scores = [|95; 87; 92|])
838+
user.Name |> equal "Test User"
839+
user.Scores |> Array.sum |> equal 274
840+
841+
[<Fact>]
842+
let ``test Pydantic model_dump with FSharpArray`` () =
843+
// Test that Pydantic can serialize (dump) a model containing FSharpArray
844+
let user = PydanticUserWithArray(Name = "Test User", Scores = [|95; 87; 92|])
845+
// Call model_dump() to serialize to dict
846+
let dumped: obj = emitPyExpr (user) "$0.model_dump()"
847+
// The dumped dict should have the scores as a list
848+
let scores: int[] = emitPyExpr (dumped) "$0['Scores']"
849+
scores |> Array.sum |> equal 274
850+
851+
[<Fact>]
852+
let ``test Pydantic model_dump_json with FSharpArray`` () =
853+
// Test that Pydantic can serialize to JSON a model containing FSharpArray
854+
let user = PydanticUserWithArray(Name = "Test User", Scores = [|95; 87; 92|])
855+
// Call model_dump_json() to serialize to JSON string
856+
let json: string = emitPyExpr (user) "$0.model_dump_json()"
857+
// The JSON should contain the scores
858+
json.Contains("95") |> equal true
859+
json.Contains("87") |> equal true
860+
json.Contains("92") |> equal true
861+
862+
[<Py.ClassAttributes(style=Py.ClassAttributeStyle.Attributes, init=false)>]
863+
type PydanticUserWithStringArray(Name: string, Tags: string[]) =
864+
inherit BaseModel()
865+
member val Name: string = Name with get, set
866+
member val Tags: string[] = Tags with get, set
867+
868+
[<Fact>]
869+
let ``test Pydantic serialization of FSharpArray with strings`` () =
870+
// Test that string arrays work with Pydantic
871+
let user = PydanticUserWithStringArray(Name = "Test User", Tags = [|"admin"; "active"; "verified"|])
872+
user.Name |> equal "Test User"
873+
user.Tags |> Array.length |> equal 3
874+
875+
[<Fact>]
876+
let ``test Pydantic model_dump with string array`` () =
877+
// Test that Pydantic can serialize string arrays
878+
let user = PydanticUserWithStringArray(Name = "Test User", Tags = [|"admin"; "active"|])
879+
let dumped: obj = emitPyExpr (user) "$0.model_dump()"
880+
let tags: string[] = emitPyExpr (dumped) "$0['Tags']"
881+
tags.[0] |> equal "admin"
882+
tags.[1] |> equal "active"
883+
884+
[<Py.ClassAttributes(style=Py.ClassAttributeStyle.Attributes, init=false)>]
885+
type PydanticUserWithFloatArray(Name: string, Values: float[]) =
886+
inherit BaseModel()
887+
member val Name: string = Name with get, set
888+
member val Values: float[] = Values with get, set
889+
890+
[<Fact>]
891+
let ``test Pydantic serialization of FSharpArray with floats`` () =
892+
// Test that float arrays work with Pydantic
893+
let user = PydanticUserWithFloatArray(Name = "Test User", Values = [|1.5; 2.5; 3.5|])
894+
user.Name |> equal "Test User"
895+
user.Values |> Array.sum |> equal 7.5
896+
897+
[<Fact>]
898+
let ``test Pydantic model_dump_json with float array`` () =
899+
// Test that Pydantic can serialize float arrays to JSON
900+
let user = PydanticUserWithFloatArray(Name = "Test User", Values = [|1.5; 2.5|])
901+
let json: string = emitPyExpr (user) "$0.model_dump_json()"
902+
json.Contains("1.5") |> equal true
903+
json.Contains("2.5") |> equal true
904+
824905
#endif

0 commit comments

Comments
 (0)