Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"rollForward": false
},
"fable": {
"version": "5.1.0",
"version": "5.2.0",
"commands": [
"fable"
],
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ requires-python = ">= 3.12, < 4"
readme = "README.md"
license = "MIT"
dependencies = [
"fable-library==5.1.0",
"fable-library==5.2.0",
"pyright>=1.1.410",
]

Expand Down
2 changes: 1 addition & 1 deletion src/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description = "Python bindings for Fable"
authors = [{ name = "Dag Brattli", email = "dag@brattli.net" }]
requires-python = ">= 3.12, < 4.0"
license = "MIT"
dependencies = ["fable-library>=0.8.0"]
dependencies = ["fable-library>=5.0.0,<6"]

[build-system]
requires = ["hatchling"]
Expand Down
2 changes: 1 addition & 1 deletion src/stdlib/Collections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ type deque<'T>() =
member _.Item(index: int) : 'T = nativeOnly

/// Maximum length of the deque, or None if unbounded
member _.maxlen : int option = nativeOnly
member _.maxlen: int option = nativeOnly

/// Add item to the right end
member _.append(item: 'T) : unit = nativeOnly
Expand Down
5 changes: 5 additions & 0 deletions src/stdlib/Os.fs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,18 @@ type IExports =
/// Recursive directory creation function
/// See https://docs.python.org/3/library/os.html#os.makedirs
abstract makedirs: path: string -> unit

/// Recursive directory creation, creating parent directories as needed.
/// Raises FileExistsError if the directory already exists and exist_ok is false.
/// See https://docs.python.org/3/library/os.html#os.makedirs
[<Emit("$0.makedirs($1, exist_ok=$2)")>]
abstract makedirs: path: string * exist_ok: bool -> unit

/// Recursive directory creation with explicit mode and exist_ok flag.
/// See https://docs.python.org/3/library/os.html#os.makedirs
[<Emit("$0.makedirs($1, $2, $3)")>]
abstract makedirs: path: string * mode: int * exist_ok: bool -> unit

/// Set the environment variable named key to the string value
/// See https://docs.python.org/3/library/os.html#os.putenv
abstract putenv: key: string * value: string -> unit
Expand All @@ -77,10 +80,12 @@ type IExports =
/// to prune the search or impose a specific visiting order.
/// See https://docs.python.org/3/library/os.html#os.walk
abstract walk: top: string -> seq<string * ResizeArray<string> * ResizeArray<string>>

/// Walk a directory tree top-down or bottom-up (topdown=false).
/// See https://docs.python.org/3/library/os.html#os.walk
[<Emit("$0.walk($1, topdown=$2)")>]
abstract walk: top: string * topdown: bool -> seq<string * ResizeArray<string> * ResizeArray<string>>

/// Test whether a path exists
/// See https://docs.python.org/3/library/os.path.html#os.path.exists
abstract path: PathModule
Expand Down
4 changes: 2 additions & 2 deletions src/stdlib/Pathlib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ type Path() =
/// This mirrors Python's ``path / "child"`` operator.
/// See https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.__truediv__
[<Emit("$0 / $1")>]
static member (/) (left: Path, right: string) : Path = nativeOnly
static member (/)(left: Path, right: string) : Path = nativeOnly

/// Return a new Path by appending another Path.
/// See https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.__truediv__
[<Emit("$0 / $1")>]
static member (/) (left: Path, right: Path) : Path = nativeOnly
static member (/)(left: Path, right: Path) : Path = nativeOnly

/// Join one or more path segments to this path and return the result.
/// See https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.joinpath
Expand Down
1 change: 1 addition & 0 deletions src/stdlib/Threading.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type Event() =
/// Set the internal flag to true
[<Emit("$0.set()")>]
member _.set() : unit = nativeOnly

/// Reset the internal flag to false
member _.clear() : unit = nativeOnly
/// Return true if and only if the internal flag is true
Expand Down
4 changes: 1 addition & 3 deletions test/TestBuiltins.fs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,7 @@ let ``test len with string works`` () =

[<Fact>]
let ``test map with single iterable works`` () =
builtins.map ((fun x -> x * 2), [ 1; 2; 3 ])
|> Seq.toList
|> equal [ 2; 4; 6 ]
builtins.map ((fun x -> x * 2), [ 1; 2; 3 ]) |> Seq.toList |> equal [ 2; 4; 6 ]

[<Fact>]
let ``test map with two iterables works`` () =
Expand Down
129 changes: 67 additions & 62 deletions test/TestCollections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,150 +27,155 @@ let ``test Counter missing key returns 0`` () =
[<Fact>]
let ``test Counter most_common returns all elements sorted by count`` () =
let c = Counter.ofSeq [ "a"; "b"; "a"; "c"; "a"; "b" ]
let top = c.most_common() |> Seq.head
let top = c.most_common () |> Seq.head
top |> equal ("a", 3)

[<Fact>]
let ``test Counter most_common n returns top n elements`` () =
let c = Counter.ofSeq [ "a"; "b"; "a"; "c"; "a"; "b" ]
let topTwo = c.most_common(2) |> Seq.toList
let topTwo = c.most_common (2) |> Seq.toList
topTwo |> List.length |> equal 2
topTwo |> List.head |> equal ("a", 3)

[<Fact>]
let ``test Counter elements returns repeated sequence`` () =
let c = Counter.ofSeq [ "a"; "a"; "b" ]
let elems = c.elements() |> Seq.toList |> List.sort
let elems = c.elements () |> Seq.toList |> List.sort
elems |> equal [ "a"; "a"; "b" ]

[<Fact>]
let ``test Counter total sums all counts`` () =
let c = Counter.ofSeq [ "a"; "b"; "a"; "c" ]
c.total() |> equal 4
c.total () |> equal 4

[<Fact>]
let ``test Counter update adds counts`` () =
let c = Counter.ofSeq [ "a"; "b" ]
c.update([ "a"; "c" ])
c.update ([ "a"; "c" ])
c.Item("a") |> equal 2
c.Item("c") |> equal 1

[<Fact>]
let ``test Counter subtract reduces counts`` () =
let c = Counter.ofSeq [ "a"; "a"; "b" ]
c.subtract([ "a" ])
c.subtract ([ "a" ])
c.Item("a") |> equal 1

[<Fact>]
let ``test Counter contains reflects key presence`` () =
let c = Counter.ofSeq [ "a"; "b" ]
c.contains("a") |> equal true
c.contains("z") |> equal false
c.contains ("a") |> equal true
c.contains ("z") |> equal false

[<Fact>]
let ``test Counter keys and values enumerate the counter`` () =
let c = Counter.ofSeq [ "a"; "b"; "a" ]
c.keys() |> Seq.toList |> List.sort |> equal [ "a"; "b" ]
c.values() |> Seq.sum |> equal 3
c.keys () |> Seq.toList |> List.sort |> equal [ "a"; "b" ]
c.values () |> Seq.sum |> equal 3

[<Fact>]
let ``test Counter pop removes and returns count`` () =
let c = Counter.ofSeq [ "a"; "a"; "b" ]
c.pop("a") |> equal 2
c.contains("a") |> equal false
c.pop ("a") |> equal 2
c.contains ("a") |> equal false

// ============================================================================
// defaultdict tests
// ============================================================================

[<Fact>]
let ``test defaultdict missing key invokes factory`` () =
let d = defaultdict<string, ResizeArray<int>>.withFactory(fun () -> ResizeArray())
let d = defaultdict<string, ResizeArray<int>>.withFactory (fun () -> ResizeArray())
let list = d.Item("key")
list.Count |> equal 0

[<Fact>]
let ``test defaultdict factory creates separate instances`` () =
let d = defaultdict<string, ResizeArray<int>>.withFactory(fun () -> ResizeArray())
let d = defaultdict<string, ResizeArray<int>>.withFactory (fun () -> ResizeArray())
let list1 = d.Item("a")
list1.Add(1)
let list2 = d.Item("b")
list2.Count |> equal 0

[<Fact>]
let ``test defaultdict int factory starts at zero`` () =
let d = defaultdict<string, int>.withFactory(fun () -> 0)
let d = defaultdict<string, int>.withFactory (fun () -> 0)
d.Item("key") |> equal 0

[<Fact>]
let ``test defaultdict get returns None for missing key without invoking factory`` () =
let mutable factoryCalled = false
let d = defaultdict<string, int>.withFactory(fun () -> factoryCalled <- true; 0)
let result = d.get("missing")

let d =
defaultdict<string, int>.withFactory (fun () ->
factoryCalled <- true
0)

let result = d.get ("missing")
result |> equal None
factoryCalled |> equal false

[<Fact>]
let ``test defaultdict get with default returns default for missing key`` () =
let d = defaultdict<string, int>.withFactory(fun () -> 0)
d.get("missing", 42) |> equal 42
let d = defaultdict<string, int>.withFactory (fun () -> 0)
d.get ("missing", 42) |> equal 42

[<Fact>]
let ``test defaultdict contains returns false for missing key`` () =
let d = defaultdict<string, int>.withFactory(fun () -> 0)
d.contains("key") |> equal false
let d = defaultdict<string, int>.withFactory (fun () -> 0)
d.contains ("key") |> equal false

[<Fact>]
let ``test defaultdict contains returns true after access`` () =
let d = defaultdict<string, int>.withFactory(fun () -> 99)
let d = defaultdict<string, int>.withFactory (fun () -> 99)
let _ = d.Item("key")
d.contains("key") |> equal true
d.contains ("key") |> equal true

// ============================================================================
// deque tests
// ============================================================================

[<Fact>]
let ``test deque empty deque has length 0`` () =
let d = deque<int>()
d.length() |> equal 0
let d = deque<int> ()
d.length () |> equal 0

[<Fact>]
let ``test deque ofSeq creates deque from sequence`` () =
let d = deque.ofSeq [ 1; 2; 3 ]
d.length() |> equal 3
d.length () |> equal 3

[<Fact>]
let ``test deque append adds to right`` () =
let d = deque.ofSeq [ 1; 2 ]
d.append(3)
d.append (3)
d.Item(2) |> equal 3

[<Fact>]
let ``test deque appendleft adds to left`` () =
let d = deque.ofSeq [ 1; 2 ]
d.appendleft(0)
d.appendleft (0)
d.Item(0) |> equal 0
d.length() |> equal 3
d.length () |> equal 3

[<Fact>]
let ``test deque pop removes from right`` () =
let d = deque.ofSeq [ 1; 2; 3 ]
let v = d.pop()
let v = d.pop ()
v |> equal 3
d.length() |> equal 2
d.length () |> equal 2

[<Fact>]
let ``test deque popleft removes from left`` () =
let d = deque.ofSeq [ 1; 2; 3 ]
let v = d.popleft()
let v = d.popleft ()
v |> equal 1
d.length() |> equal 2
d.length () |> equal 2

[<Fact>]
let ``test deque rotate shifts elements right`` () =
let d = deque.ofSeq [ 1; 2; 3; 4; 5 ]
d.rotate(2)
d.rotate (2)
d.Item(0) |> equal 4
d.Item(1) |> equal 5

Expand All @@ -181,29 +186,29 @@ let ``test deque maxlen is None for unbounded deque`` () =

[<Fact>]
let ``test deque withMaxlen creates bounded deque`` () =
let d = deque<int>.withMaxlen(3)
d.append(1)
d.append(2)
d.append(3)
d.append(4) // should push out 1
d.length() |> equal 3
let d = deque<int>.withMaxlen (3)
d.append (1)
d.append (2)
d.append (3)
d.append (4) // should push out 1
d.length () |> equal 3
d.Item(0) |> equal 2

[<Fact>]
let ``test deque ofSeq with maxlen creates bounded deque`` () =
let d = deque.ofSeq ([ 1; 2; 3; 4; 5 ], 3)
d.length() |> equal 3
d.length () |> equal 3
d.maxlen |> equal (Some 3)

[<Fact>]
let ``test deque count occurrences`` () =
let d = deque.ofSeq [ 1; 2; 1; 3; 1 ]
d.count(1) |> equal 3
d.count (1) |> equal 3

[<Fact>]
let ``test deque extendleft reverses iterable order`` () =
let d = deque.ofSeq [ 3 ]
d.extendleft([ 1; 2 ])
d.extendleft ([ 1; 2 ])
// Each element pushed onto the left in turn => final order [2; 1; 3]
d.Item(0) |> equal 2
d.Item(1) |> equal 1
Expand All @@ -216,43 +221,43 @@ let ``test deque extendleft reverses iterable order`` () =
[<Fact>]
let ``test OrderedDict preserves insertion order`` () =
let od = OrderedDict<string, int>()
od.set("a", 1)
od.set("b", 2)
od.set("c", 3)
od.keys() |> Seq.toList |> equal [ "a"; "b"; "c" ]
od.set ("a", 1)
od.set ("b", 2)
od.set ("c", 3)
od.keys () |> Seq.toList |> equal [ "a"; "b"; "c" ]

[<Fact>]
let ``test OrderedDict get existing key`` () =
let od = OrderedDict<string, int>()
od.set("x", 42)
od.set ("x", 42)
od.Item("x") |> equal 42

[<Fact>]
let ``test OrderedDict get returns None for missing key`` () =
let od = OrderedDict<string, int>()
od.get("missing") |> equal None
od.get ("missing") |> equal None

[<Fact>]
let ``test OrderedDict move_to_end moves last element`` () =
let od = OrderedDict<string, int>()
od.set("a", 1)
od.set("b", 2)
od.set("c", 3)
od.move_to_end("a")
od.keys() |> Seq.toList |> equal [ "b"; "c"; "a" ]
od.set ("a", 1)
od.set ("b", 2)
od.set ("c", 3)
od.move_to_end ("a")
od.keys () |> Seq.toList |> equal [ "b"; "c"; "a" ]

[<Fact>]
let ``test OrderedDict move_to_end with last false moves to front`` () =
let od = OrderedDict<string, int>()
od.set("a", 1)
od.set("b", 2)
od.set("c", 3)
od.move_to_end("c", false)
od.keys() |> Seq.toList |> equal [ "c"; "a"; "b" ]
od.set ("a", 1)
od.set ("b", 2)
od.set ("c", 3)
od.move_to_end ("c", false)
od.keys () |> Seq.toList |> equal [ "c"; "a"; "b" ]

[<Fact>]
let ``test OrderedDict contains returns correct result`` () =
let od = OrderedDict<string, int>()
od.set("a", 1)
od.contains("a") |> equal true
od.contains("b") |> equal false
od.set ("a", 1)
od.contains ("a") |> equal true
od.contains ("b") |> equal false
Loading
Loading