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