From 17dc276cad75caa6a7b6a94f4609deb632cb4e69 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 1 Mar 2026 16:39:24 +0100 Subject: [PATCH 1/2] [Python] Fix __hash__ to return native int for Python 3.14 compatibility combine_hash_codes and array_hash returned int32 (Fable's Int32, not a subclass of native int), and TypeInfo.__hash__ passed that through. Python 3.14 strictly requires __hash__ to return a native int. Change combine_hash_codes to operate on native int throughout, matching the pattern already established in HashableBase.__hash__. Co-Authored-By: Claude Opus 4.6 --- src/Fable.Cli/CHANGELOG.md | 1 + src/Fable.Compiler/CHANGELOG.md | 1 + src/fable-library-py/fable_library/reflection.py | 6 +++--- src/fable-library-py/fable_library/util.py | 10 +++++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 4c7dd6cd6..a938df014 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix `__hash__` to return native `int` instead of `int32` for Python 3.14 compatibility (by @dbrattli) * [Python] Fix PyPI publish workflow version pattern to support `rc` tags (by @dbrattli) * [Beam] Bundle `fable-library-beam` in NuGet package so `dotnet fable --lang beam` works (by @dbrattli) * [Beam] Fix optional arguments by unwrapping at call sites and padding missing trailing args (by @dbrattli) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index f67a1c0dd..1e4b5ea56 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix `__hash__` to return native `int` instead of `int32` for Python 3.14 compatibility (by @dbrattli) * [Beam] Fix optional arguments by unwrapping at call sites and padding missing trailing args (by @dbrattli) * [Beam] Fix generic constraint interface dispatch (by @dbrattli) * [Beam] Fix class constructor field invokes and explicit val fields (by @dbrattli) diff --git a/src/fable-library-py/fable_library/reflection.py b/src/fable-library-py/fable_library/reflection.py index 73317a5d4..90ea8fb10 100644 --- a/src/fable-library-py/fable_library/reflection.py +++ b/src/fable-library-py/fable_library/reflection.py @@ -6,7 +6,7 @@ from typing import Any, cast from .array_ import Array -from .core import FSharpRef, int32 +from .core import FSharpRef from .record import Record from .types import IntegerTypes from .union import Union @@ -55,8 +55,8 @@ def __eq__(self, other: Any) -> bool: return equals(self, other) def __hash__(self) -> int: - hashes: list[int32] = [int32(hash(x)) for x in self.generics or []] - hashes.append(int32(hash(self.fullname))) + hashes: list[int] = [hash(x) for x in self.generics or []] + hashes.append(hash(self.fullname)) return combine_hash_codes(hashes) diff --git a/src/fable-library-py/fable_library/util.py b/src/fable-library-py/fable_library/util.py index 4d8fbb496..cf9bb72d5 100644 --- a/src/fable-library-py/fable_library/util.py +++ b/src/fable-library-py/fable_library/util.py @@ -645,9 +645,9 @@ def identity_hash(x: Any) -> int32: return number_hash(ObjectRef.id(x)) -def combine_hash_codes(hashes: list[int32]) -> int32: +def combine_hash_codes(hashes: list[int]) -> int: if not hashes: - return int32(0) + return 0 return functools.reduce(lambda h1, h2: ((h1 << 5) + h1) ^ h2, hashes) @@ -656,10 +656,10 @@ def structural_hash(x: Any) -> int32: return int32(hash(x)) -def array_hash(xs: Iterable[object]) -> int32: - hashes: list[int32] = [] +def array_hash(xs: Iterable[object]) -> int: + hashes: list[int] = [] for x in xs: - hashes.append(structural_hash(x)) + hashes.append(hash(x)) return combine_hash_codes(hashes) From 1e061f1a963ea50321327761dde39d2a70508f3e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 1 Mar 2026 16:47:57 +0100 Subject: [PATCH 2/2] [Python] Fix TypeInfo.__hash__ to return native int for Python 3.14 TypeInfo.__hash__ called combine_hash_codes() which returns int32 (Fable's Int32, not a subclass of native int) and passed it through. Python 3.14 strictly requires __hash__ to return a native int. Wrap the result with int(), matching the pattern in HashableBase.__hash__. combine_hash_codes and array_hash stay as int32 since they serve GetHashCode (which returns int32 by design). Co-Authored-By: Claude Opus 4.6 --- src/fable-library-py/fable_library/reflection.py | 8 ++++---- src/fable-library-py/fable_library/util.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fable-library-py/fable_library/reflection.py b/src/fable-library-py/fable_library/reflection.py index 90ea8fb10..8366b2942 100644 --- a/src/fable-library-py/fable_library/reflection.py +++ b/src/fable-library-py/fable_library/reflection.py @@ -6,7 +6,7 @@ from typing import Any, cast from .array_ import Array -from .core import FSharpRef +from .core import FSharpRef, int32 from .record import Record from .types import IntegerTypes from .union import Union @@ -55,9 +55,9 @@ def __eq__(self, other: Any) -> bool: return equals(self, other) def __hash__(self) -> int: - hashes: list[int] = [hash(x) for x in self.generics or []] - hashes.append(hash(self.fullname)) - return combine_hash_codes(hashes) + hashes: list[int32] = [int32(hash(x)) for x in self.generics or []] + hashes.append(int32(hash(self.fullname))) + return int(combine_hash_codes(hashes)) def class_type( diff --git a/src/fable-library-py/fable_library/util.py b/src/fable-library-py/fable_library/util.py index cf9bb72d5..4d8fbb496 100644 --- a/src/fable-library-py/fable_library/util.py +++ b/src/fable-library-py/fable_library/util.py @@ -645,9 +645,9 @@ def identity_hash(x: Any) -> int32: return number_hash(ObjectRef.id(x)) -def combine_hash_codes(hashes: list[int]) -> int: +def combine_hash_codes(hashes: list[int32]) -> int32: if not hashes: - return 0 + return int32(0) return functools.reduce(lambda h1, h2: ((h1 << 5) + h1) ^ h2, hashes) @@ -656,10 +656,10 @@ def structural_hash(x: Any) -> int32: return int32(hash(x)) -def array_hash(xs: Iterable[object]) -> int: - hashes: list[int] = [] +def array_hash(xs: Iterable[object]) -> int32: + hashes: list[int32] = [] for x in xs: - hashes.append(hash(x)) + hashes.append(structural_hash(x)) return combine_hash_codes(hashes)