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
1 change: 0 additions & 1 deletion pyrightconfig.ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"**/node_modules/**",
"temp/tests/Python/test_applicative.py",
"temp/tests/Python/test_hash_set.py",
"temp/tests/Python/test_mailbox_processor.py",
"temp/tests/Python/test_nested_and_recursive_pattern.py",
"temp/tests/Python/fable_modules/thoth_json_python/encode.py"
]
Expand Down
4 changes: 4 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [Beam] Add Erlang/BEAM target (`--lang beam`). Compiles F# to `.erl` source files. 2086 tests passing. (by @dbrattli)

### Fixed

* [Python] Fix type var scoping for PEP 695 annotations: emit `Any` for type vars outside function scope and prevent non-repeated generic params from leaking into `ScopedTypeParams` (by @dbrattli)

## 5.0.0-alpha.24 - 2026-02-13

### Fixed
Expand Down
4 changes: 4 additions & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [Beam] Add Erlang/BEAM compilation support: Fable2Beam transform, Beam Replacements, ErlangPrinter, and 31 runtime `.erl` modules. 2086 tests passing. (by @dbrattli)

### Fixed

* [Python] Fix type var scoping for PEP 695 annotations: emit `Any` for type vars outside function scope and prevent non-repeated generic params from leaking into `ScopedTypeParams` (by @dbrattli)

## 5.0.0-alpha.23 - 2026-02-13

### Fixed
Expand Down
6 changes: 4 additions & 2 deletions src/Fable.Transforms/Python/Fable2Python.Annotation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,10 @@ let rec typeAnnotation
com.AddTypeVar(ctx, name), []
| Some _ -> stdlibModuleTypeHint com ctx "typing" "Any" [] repeatedGenerics
| None ->
let name = Helpers.clean name
com.AddTypeVar(ctx, name), []
// No repeatedGenerics info means we're outside a function type param scope
// (e.g., class fields, variable annotations). With PEP 695, type vars are
// lexically scoped, so emit Any instead of an undefined type var reference.
stdlibModuleTypeHint com ctx "typing" "Any" [] repeatedGenerics
| Fable.Unit -> Expression.none, []
| Fable.Boolean -> Expression.name "bool", []
| Fable.Char -> Expression.name "str", []
Expand Down
37 changes: 31 additions & 6 deletions src/Fable.Transforms/Python/Fable2Python.Transforms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3022,8 +3022,11 @@ let transformFunction
let mutable isTailCallOptimized = false

let argTypes = args |> List.map (fun id -> id.Type)
let genTypeParams = getGenericTypeParams (argTypes @ [ body.Type ])
let newTypeParams = Set.difference genTypeParams ctx.ScopedTypeParams
// Only track actually-declared (repeated) generic params in ScopedTypeParams.
// With PEP 695, type params are lexically scoped, so only params that appear
// in the function's [] declaration should be tracked. Non-repeated params
// (erased to Any) must not enter scope or inner functions will reference them.
let newTypeParams = Set.difference repeatedGenerics ctx.ScopedTypeParams

let ctx =
{ ctx with
Expand Down Expand Up @@ -3739,13 +3742,35 @@ let transformModuleFunction
(info: Fable.MemberFunctionOrValue)
(membName: string)
(fableArgs: Fable.Ident list)
body
(body: Fable.Expr)
=
let argTypes = fableArgs |> List.map _.Type

// Compute which type params will be declared in this function's PEP 695 []
// BEFORE transforming the body, so inner functions know these are already in scope
// and won't re-declare them (which would cause "TypeVar already in use" errors).
let explicitGenerics = Annotation.getMemberGenParams info.GenericParameters

let signatureGenerics =
(getGenericTypeParams (argTypes @ [ body.Type ]), ctx.ScopedTypeParams)
||> Set.difference

let declaredTypeParams =
Set.empty
|> Set.union explicitGenerics
|> Set.union signatureGenerics
|> Set.difference
<| ctx.ScopedTypeParams

let ctxWithDeclaredParams =
{ ctx with ScopedTypeParams = Set.union ctx.ScopedTypeParams declaredTypeParams }

let args, body', returnType =
getMemberArgsAndBody com ctx (NonAttached membName) info.HasSpread fableArgs body
getMemberArgsAndBody com ctxWithDeclaredParams (NonAttached membName) info.HasSpread fableArgs body

let typeParams =
Annotation.makeFunctionTypeParamsWithConstraints com ctx info.GenericParameters declaredTypeParams

let argTypes = fableArgs |> List.map _.Type
let typeParams = calculateTypeParams com ctx info argTypes body.Type
let name = com.GetIdentifier(ctx, membName |> Naming.toPythonNaming)

let isAsync = isTaskType body.Type
Expand Down
Loading