Skip to content

Commit f13e7ef

Browse files
authored
Fix Python docs: correct Py.Decorator to Py.Decorate (#227)
* Fix Python docs: correct Py.Decorator to Py.Decorate and fix examples - Rename Py.Decorator to Py.Decorate (the actual API name) - Replace record example with class example (records already compile to @DataClass automatically, and Py.Decorate cannot be used on records) - Remove function decorator example (Fable doesn't support attributes on functions, only on types) - Add single-argument form example for local decorators - Move Class Attributes section before Python Decorators since the decorator example references Py.ClassAttributes * Add ClassAttributes options table and Py.DataClass shorthand to docs Explain the different style and init parameters for Py.ClassAttributes, and document Py.DataClass as a shorthand for frameworks like Pydantic. * Add DecorateTemplate section for library authors Document Py.DecorateTemplate for creating reusable custom decorator attributes, with a web framework route decorator as an example.
1 parent 5d38a6d commit f13e7ef

1 file changed

Lines changed: 68 additions & 30 deletions

File tree

docs/docs/python/features.md

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -458,73 +458,111 @@ def add(a: int32, b: int32) -> Any:
458458

459459
`Py.python` executes as statements, so use `return` keyword to return values.
460460

461-
## Python Decorators
461+
## Class Attributes
462462

463463
<p class="tag is-info is-medium">
464464
Added in v5.0.0-alpha
465465
</p>
466466

467-
`Py.Decorator` allows you to apply Python decorators to classes and functions.
467+
`Py.ClassAttributes` controls how class members are generated in Python.
468468

469469
```fs
470470
open Fable.Core
471471
472-
[<Py.Decorator("dataclasses.dataclass")>]
473-
type User =
474-
{ Name: string
475-
Age: int }
472+
[<Py.ClassAttributes(Py.ClassAttributeStyle.Attributes)>]
473+
type Config() =
474+
member val Name = "default" with get, set
475+
member val Port = 8080 with get, set
476476
```
477477

478478
generates:
479479

480480
```py
481-
@dataclasses.dataclass
482-
class User:
483-
name: str
484-
age: int32
481+
class Config:
482+
name: str = "default"
483+
port: int = 8080
485484
```
486485

487-
You can also pass parameters to decorators:
486+
Without `ClassAttributes`, members would be generated as properties with instance backing.
488487

489-
```fs
490-
[<Py.Decorator("functools.lru_cache", "maxsize=128")>]
491-
let expensiveFunction x = x * 2
492-
```
488+
| Parameter | Effect |
489+
| -------------------- | ---------------------------------------------------------- |
490+
| `style = Attributes` | Generate class-level type annotations |
491+
| `style = Properties` | Generate properties with instance attribute backing |
492+
| `init = false` | Don't generate `__init__` (Pydantic/dataclass provides it) |
493+
| `init = true` | Generate `__init__` with attribute assignments |
493494

494-
generates:
495+
`Py.DataClass` is shorthand for `Py.ClassAttributes(style = Attributes, init = false)`.
496+
This is useful when working with frameworks like Pydantic, dataclasses, and attrs that
497+
expect class-level type annotations:
495498

496-
```py
497-
@functools.lru_cache(maxsize=128)
498-
def expensive_function(x):
499-
return x * 2
499+
```fs
500+
[<Py.DataClass>]
501+
type User() =
502+
inherit BaseModel()
503+
member val Name: string = "" with get, set
504+
member val Age: int = 0 with get, set
500505
```
501506

502-
## Class Attributes
507+
## Python Decorators
503508

504509
<p class="tag is-info is-medium">
505510
Added in v5.0.0-alpha
506511
</p>
507512

508-
`Py.ClassAttributes` controls how class members are generated in Python.
513+
`Py.Decorate` allows you to apply Python decorators to types.
509514

510515
```fs
511516
open Fable.Core
512517
513-
[<Py.ClassAttributes(Py.ClassAttributeStyle.Attributes)>]
514-
type Config() =
515-
member val Name = "default" with get, set
516-
member val Port = 8080 with get, set
518+
[<Py.Decorate("dataclass", "dataclasses")>]
519+
[<Py.ClassAttributes(Py.ClassAttributeStyle.Attributes, false)>]
520+
type DecoratedUser() =
521+
member val Name: string = "" with get, set
522+
member val Age: int = 0 with get, set
517523
```
518524

519525
generates:
520526

521527
```py
522-
class Config:
523-
name: str = "default"
524-
port: int = 8080
528+
@dataclass
529+
class DecoratedUser:
530+
Age: int32 = int32.ZERO
531+
Name: str = ""
525532
```
526533

527-
Without `ClassAttributes`, members would be generated as properties with instance backing.
534+
The single argument form can be used for local decorators where you don't need to import anything:
535+
536+
```fs
537+
[<Py.Decorate("my_decorator")>]
538+
type MyClass() =
539+
member val Value: int = 0 with get, set
540+
```
541+
542+
### DecorateTemplate for Library Authors
543+
544+
When building a library, you can create custom decorator attributes that users
545+
can apply without knowing the underlying Python syntax. Use `Py.DecorateTemplate`:
546+
547+
```fs
548+
/// Custom route decorator for a web framework
549+
[<Erase>]
550+
[<Py.DecorateTemplate("app.get('{0}')", "fastapi")>]
551+
type GetRouteAttribute(path: string) =
552+
inherit System.Attribute()
553+
```
554+
555+
Now users of your library can simply write:
556+
557+
```fs
558+
[<GetRoute("/users")>]
559+
static member get_users() = ...
560+
// Generates: @app.get('/users')
561+
```
562+
563+
The template string uses `{0}`, `{1}`, etc. as placeholders for the attribute's
564+
constructor arguments. The `[<Erase>]` attribute prevents the attribute type
565+
from being emitted to Python.
528566

529567
## `[<Erase>]`
530568

0 commit comments

Comments
 (0)