Skip to content

Conversation

dbrattli
Copy link
Collaborator

@dbrattli dbrattli commented Aug 4, 2025

Overview

Introduce a new [<ClassAttributes>] attribute that provides Python-specific control over how F# types are transpiled to Python classes. This attribute implies member attachment (similar to AttachMembers) while offering Python-specific parameters for better integration with Python frameworks like Pydantic, dataclasses, and SQLAlchemy.

Current AttachMembers Behavior (For Context)

Without AttachMembers (Default)

type User() =
    member val Name: string = "" with get, set

Generates:

class User:
    def __init__(self, __unit: None=None) -> None:
        pass

def User_get_Name(self) -> str:
    return self._Name

def User_set_Name(self, value: str) -> None:
    self._Name = value

With AttachMembers (Current)

[<AttachMembers>]
type User() =
    member val Name: string = "" with get, set

Generates:

class User:
    def __init__(self, __unit: None=None) -> None:
        self._Name: str = ""

    @property
    def Name(self) -> str:
        return self._Name

    @Name.setter
    def Name(self, value: str) -> None:
        self._Name = value

New ClassAttributes Attribute

Basic Syntax

// Replaces [<AttachMembers>] for Python with same behavior (properties style)
[<ClassAttributes>]
type User() = ...

// New: Generate class attributes instead of @property
[<ClassAttributes(style="attributes")>]
type User() = ...

// With additional Python-specific parameters
[<ClassAttributes(style="attributes", init=true)>]
type Config() = ...

Style: "attributes" (New Feature)

F# Input

[<ClassAttributes(style="attributes")>]
type User() =
    member val Name: string = "" with get, set
    member val Age: int = 0 with get, set
    member val Email: string option = None with get, set

Generated Output

class User:
    Name: str
    Age: int
    Email: str | None

    def __init__(self, Name: str = "", Age: int = 0, Email: str | None = None):
        self.Name = Name
        self.Age = Age
        self.Email = Email

Parameter Reference

Parameter Type Default Description
style string "properties" "properties" (current behavior) or "attributes"
init bool true Generate __init__ method

Parameter Examples

Suppress init Generation

[<ClassAttributes(style="attributes", init=false)>]
type Config() =
    member val Debug: bool = false with get, set
    member val Timeout: int = 30 with get, set
class Config:
    Debug: bool = False
    Timeout: int = 30
    # No __init__ method generated

@dbrattli dbrattli changed the title [Python] Add [<ClassAttributes>] attribute to control Python class generation [Python] Add [<ClassAttributes>] to control Python class generation Aug 4, 2025
@dbrattli dbrattli merged commit 39c83e8 into main Aug 5, 2025
20 checks passed
@dbrattli dbrattli deleted the python-class-attributes branch August 5, 2025 14:22
@MangelMaxime
Copy link
Member

MangelMaxime commented Aug 11, 2025

@dbrattli We probably need to update the documentation too.

Also, wouldn't it be better to replace the string with a DU, for example something like:

[<ClassAttributes(style=ClassAttributesStyles.Attributes, init=false)>]

this way it avoid potential typo error?

@dbrattli
Copy link
Collaborator Author

But attribute values need afaik to be basic types. Then the alternative is to use an enum: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes#2224-attribute-parameter-types

@MangelMaxime
Copy link
Member

I though I remembered something on that line but didn't check 🙈

However, we can use an enum to achieve something similar to DUs. This is what we do for StringEnum for example

[<RequireQualifiedAccess>]
type ClassAttributeStyle =
    | Properties = 0
    | Attributes = 1

[<AttributeUsage(AttributeTargets.Class)>]
type ClassAttributes() =
    inherit Attribute()

    new(style: ClassAttributeStyle) = ClassAttributes()

    new(style: ClassAttributeStyle, init: bool) = ClassAttributes()

[<ClassAttributes(style = ClassAttributeStyle.Properties)>]
type MyClass () =
    class end

@dbrattli
Copy link
Collaborator Author

Thanks. Will fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants