Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

* [Python] Fix object expressions implementing interfaces with `[<CLIEvent>]` members no longer produce unimplementable abstract Protocol members (fixes #3039)

## 5.0.0-rc.7 - 2026-04-07

### Added
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 @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

* [Python] Fix object expressions implementing interfaces with `[<CLIEvent>]` members no longer produce unimplementable abstract Protocol members (fixes #3039)

## 5.0.0-rc.13 - 2026-04-07

### Added
Expand Down
18 changes: 18 additions & 0 deletions src/Fable.Transforms/Python/Fable2Python.Transforms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4428,6 +4428,24 @@
|> List.filter (fun memb -> gr.Length = 1 || (memb.IsGetter || memb.IsSetter))
)

// [<CLIEvent>] properties are represented in FCS as both an event property getter (e.g.
// "Event" with CompiledName "get_Event") and add_/remove_ accessor methods. Object
// expressions only implement the add_/remove_ methods, so we must exclude the event
// property getter from the Protocol to avoid an unimplementable abstract member.
let eventPropertyNames =
members
|> List.choose (fun m ->
if m.CompiledName.StartsWith("add_") then

Check warning

Code scanning / Ionide.Analyzers.Cli

Verifies the correct usage of System.String.StartsWith Warning

The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Some(m.CompiledName.Substring(4))
else
None
)
|> Set.ofList

let members =
members
|> List.filter (fun m -> not (eventPropertyNames.Contains(m.DisplayName)))

let classMembers =
[
for memb in members do
Expand Down
34 changes: 17 additions & 17 deletions tests/Python/TestEvent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -225,23 +225,23 @@ let ``test Classes can trigger CLI events on interfaces`` () =
equal 5 result
equal (box classWithEvent) sender

// [<Fact>]
// let ``test Generic interface expression can have CLI events`` () =
// let mutable actualSender = ""
// let mutable result = false
// let event = Event<_,_>()
// let ifaceWIthEvent =
// { new InterfaceWithCLIEvent<_> with
// [<CLIEvent>]
// member _.Event = event.Publish }
// ifaceWIthEvent.Event.AddHandler(fun sender arg ->
// actualSender <- string sender
// result <- arg)
// let expectedSender = "SENDER"
// let expectedResult = true
// event.Trigger(expectedSender, expectedResult)
// equal expectedSender actualSender
// equal expectedResult result
[<Fact>]
let ``test Generic interface expression can have CLI events`` () = // See #3039
let mutable actualSender = ""
let mutable result = false
let event = Event<_,_>()
let ifaceWIthEvent =
{ new InterfaceWithCLIEvent<_> with
[<CLIEvent>]
member _.Event = event.Publish }
ifaceWIthEvent.Event.AddHandler(fun sender arg ->
actualSender <- string sender
result <- arg)
let expectedSender = "SENDER"
let expectedResult = true
event.Trigger(expectedSender, expectedResult)
equal expectedSender actualSender
equal expectedResult result

[<Fact>]
let ``test Events are unsubscribed correctly`` () = // See #609
Expand Down
Loading