From 2219446777b83deff7bd9e6890ff6232f386ff56 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 04:07:07 +0000 Subject: [PATCH 1/2] feat(stdlib): add functools module bindings (reduce, lruCache, cache) Add Fable.Python.Functools module binding Python's functools stdlib: - reduce: fold-left with and without seed value (2 overloads) - lruCache: LRU-memoised wrapper with configurable maxsize - cache: unbounded memoised wrapper (Python 3.9+, lru_cache(maxsize=None)) Includes 6 tests covering all bindings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 4 +++ src/Fable.Python.fsproj | 1 + src/stdlib/Functools.fs | 44 ++++++++++++++++++++++++++++++++ test/Fable.Python.Test.fsproj | 1 + test/TestFunctools.fs | 48 +++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 src/stdlib/Functools.fs create mode 100644 test/TestFunctools.fs diff --git a/CHANGELOG.md b/CHANGELOG.md index bb36a98..de116ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ All notable changes to this project will be documented in this file. ## Unreleased +### 🚀 Features + +* *(stdlib)* Add functools module bindings (`reduce`, `lruCache`, `cache`) with 6 tests + ### 🐞 Bug Fixes * Fix `math.factorial` binding: changed signature from `float -> float` to `int -> int` to match Python 3.12+ where float arguments raise `TypeError`. Fixes test to use integer literals. diff --git a/src/Fable.Python.fsproj b/src/Fable.Python.fsproj index 1a155e9..4920133 100644 --- a/src/Fable.Python.fsproj +++ b/src/Fable.Python.fsproj @@ -25,6 +25,7 @@ + diff --git a/src/stdlib/Functools.fs b/src/stdlib/Functools.fs new file mode 100644 index 0000000..d41a9e7 --- /dev/null +++ b/src/stdlib/Functools.fs @@ -0,0 +1,44 @@ +/// Type bindings for Python functools module: https://docs.python.org/3/library/functools.html +module Fable.Python.Functools + +open Fable.Core + +// fsharplint:disable MemberNames + +[] +type IExports = + // ======================================================================== + // Higher-order functions + // ======================================================================== + + /// Apply a function of two arguments cumulatively to the items of an iterable, + /// reducing it to a single value (fold-left without a seed). + /// See https://docs.python.org/3/library/functools.html#functools.reduce + abstract reduce: func: ('T -> 'T -> 'T) * iterable: 'T seq -> 'T + + /// Apply a function of two arguments cumulatively to the items of an iterable, + /// starting with the initializer as the seed value (fold-left with a seed). + /// See https://docs.python.org/3/library/functools.html#functools.reduce + [] + abstract reduce: func: ('State -> 'T -> 'State) * iterable: 'T seq * initializer: 'State -> 'State + + // ======================================================================== + // Caching decorators + // ======================================================================== + + /// Wrap func with an LRU (least-recently-used) cache of at most maxsize entries. + /// Returns a memoised callable with the same signature as func. + /// Requires Python 3.8+. + /// See https://docs.python.org/3/library/functools.html#functools.lru_cache + [] + abstract lruCache: maxsize: int * func: ('T -> 'R) -> ('T -> 'R) + + /// Wrap func with an unbounded cache (equivalent to lru_cache(maxsize=None)). + /// Requires Python 3.9+. + /// See https://docs.python.org/3/library/functools.html#functools.cache + [] + abstract cache: func: ('T -> 'R) -> ('T -> 'R) + +/// Higher-order functions and operations on callable objects +[] +let functools: IExports = nativeOnly diff --git a/test/Fable.Python.Test.fsproj b/test/Fable.Python.Test.fsproj index 3c439a7..f37dca6 100644 --- a/test/Fable.Python.Test.fsproj +++ b/test/Fable.Python.Test.fsproj @@ -18,6 +18,7 @@ + diff --git a/test/TestFunctools.fs b/test/TestFunctools.fs new file mode 100644 index 0000000..a0f9bb1 --- /dev/null +++ b/test/TestFunctools.fs @@ -0,0 +1,48 @@ +module Fable.Python.Tests.Functools + +open Fable.Python.Testing +open Fable.Python.Functools + +[] +let ``test reduce sum works`` () = + functools.reduce ((fun a b -> a + b), [ 1; 2; 3; 4; 5 ]) + |> equal 15 + +[] +let ``test reduce product works`` () = + functools.reduce ((fun a b -> a * b), [ 1; 2; 3; 4; 5 ]) + |> equal 120 + +[] +let ``test reduce with initializer works`` () = + functools.reduce ((fun acc x -> acc + x), [ 1; 2; 3 ], 10) + |> equal 16 + +[] +let ``test reduce string fold with initializer works`` () = + functools.reduce ((fun acc s -> acc + s), [ "b"; "c"; "d" ], "a") + |> equal "abcd" + +[] +let ``test lruCache memoises results`` () = + let callCount = ResizeArray() + let expensive (x: int) = + callCount.Add x + x * x + let cached = functools.lruCache (128, expensive) + cached 5 |> equal 25 + cached 5 |> equal 25 + cached 3 |> equal 9 + callCount.Count |> equal 2 + +[] +let ``test cache memoises results`` () = + let callCount = ResizeArray() + let expensive (x: int) = + callCount.Add x + x * 2 + let cached = functools.cache expensive + cached 7 |> equal 14 + cached 7 |> equal 14 + cached 4 |> equal 8 + callCount.Count |> equal 2 From b2cd424debeeca6863a9a93f60e021b9afa81512 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 19 Apr 2026 15:58:36 +0200 Subject: [PATCH 2/2] fix(stdlib): revert CHANGELOG edit and align reduce callback with Itertools - Revert CHANGELOG.md (AGENTS.md forbids PR edits to it). - Use System.Func<_,_,_> for reduce's binary callback to match the accumulate convention in Itertools and ensure Fable uncurries the callable before Python invokes it with two positional arguments. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 4 ---- src/stdlib/Functools.fs | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de116ad..bb36a98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,10 +23,6 @@ All notable changes to this project will be documented in this file. ## Unreleased -### 🚀 Features - -* *(stdlib)* Add functools module bindings (`reduce`, `lruCache`, `cache`) with 6 tests - ### 🐞 Bug Fixes * Fix `math.factorial` binding: changed signature from `float -> float` to `int -> int` to match Python 3.12+ where float arguments raise `TypeError`. Fixes test to use integer literals. diff --git a/src/stdlib/Functools.fs b/src/stdlib/Functools.fs index d41a9e7..f8b8db0 100644 --- a/src/stdlib/Functools.fs +++ b/src/stdlib/Functools.fs @@ -14,13 +14,13 @@ type IExports = /// Apply a function of two arguments cumulatively to the items of an iterable, /// reducing it to a single value (fold-left without a seed). /// See https://docs.python.org/3/library/functools.html#functools.reduce - abstract reduce: func: ('T -> 'T -> 'T) * iterable: 'T seq -> 'T + abstract reduce: func: System.Func<'T, 'T, 'T> * iterable: 'T seq -> 'T /// Apply a function of two arguments cumulatively to the items of an iterable, /// starting with the initializer as the seed value (fold-left with a seed). /// See https://docs.python.org/3/library/functools.html#functools.reduce [] - abstract reduce: func: ('State -> 'T -> 'State) * iterable: 'T seq * initializer: 'State -> 'State + abstract reduce: func: System.Func<'State, 'T, 'State> * iterable: 'T seq * initializer: 'State -> 'State // ======================================================================== // Caching decorators