Skip to content

Commit 019cf3a

Browse files
committed
docs: Update docs for Python in Fable v5
1 parent d8e87bb commit 019cf3a

File tree

2 files changed

+174
-24
lines changed

2 files changed

+174
-24
lines changed

docs/docs/python/build-and-run.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,30 @@ title: Build and Run
33
layout: standard
44
---
55

6-
When targetting python, you can use the output of Fable directly by running it with the python interpreter.
6+
## Python Version
7+
8+
Fable targets Python 3.12 or higher. Python 3.14 is also supported.
9+
10+
Python 3.10 and 3.11 are deprecated.
11+
12+
## Running Python Code
13+
14+
When targeting python, you can use the output of Fable directly by running it with the python interpreter.
715

816
For example:
917

1018
```bash
1119
python3 Program.py
1220
```
21+
22+
## Publishing to PyPI
23+
24+
When building a library that uses Fable Python and you want to publish it to PyPI, you cannot bundle the Fable library the normal way. This is because the Fable library is partially written in Rust and needs to be built for all architectures.
25+
26+
Instead, use the `--fableLib` option to reference a pre-built Fable library:
27+
28+
```bash
29+
fable --lang python --fableLib fable-library
30+
```
31+
32+
This will make any reference to the Fable library point to a package in the Python search path (e.g., site-packages) instead of the normally bundled library. Your package will then need to declare `fable-library` as a dependency so users can install it from PyPI.

docs/docs/python/features.md

Lines changed: 153 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Python target is in beta meaning that breaking changes can happen between minor
1818
When targetting Python, Fable will automatically convert F# camelCase names to Python snake_case names.
1919

2020
```fs
21-
let addTwoNumber x y =
21+
let addTwoNumber x y =
2222
x + y
2323
```
2424

@@ -29,6 +29,36 @@ def add_two_number(x: int, y: int) -> int:
2929
return x + y
3030
```
3131

32+
Records snake-case all member fields:
33+
34+
<p class="tag is-info is-medium">
35+
Added in v5.0.0-alpha.14
36+
</p>
37+
38+
```fs
39+
type User = { FirstName: string }
40+
```
41+
42+
generates:
43+
44+
```py
45+
class User:
46+
def __init__(self, first_name: str):
47+
self.first_name = first_name
48+
```
49+
50+
Anonymous records preserve original casing:
51+
52+
```fs
53+
let user = {| FirstName = "John" |}
54+
```
55+
56+
generates:
57+
58+
```py
59+
user = {"FirstName": "John"}
60+
```
61+
3262
### `nativeOnly`
3363

3464
`nativeOnly` provide a dummy implementation that we use when writing bindings.
@@ -37,7 +67,7 @@ Here is its definition:
3767

3868
```fs
3969
/// Alias of nativeOnly
40-
let inline nativeOnly<'T> : 'T =
70+
let inline nativeOnly<'T> : 'T =
4171
failwith "You've hit dummy code used for Fable bindings. This probably means you're compiling Fable code to .NET by mistake, please check."
4272
```
4373

@@ -80,7 +110,7 @@ There are 2 families of imports:
80110
- Attribute-based imports
81111
- Function-based imports
82112

83-
They archieve the same goal, but with a slightly generated code.
113+
They achieve the same goal, but with a slightly generated code.
84114

85115
```fs
86116
[<Import("sayHello", "hello")>]
@@ -107,7 +137,7 @@ say_hello: Callable[[], None] = say_hello_1
107137
say_hello()
108138
```
109139

110-
Using the attribute-based imports is recommanded as it generates a smaller output.
140+
Using the attribute-based imports is recommended as it generates a smaller output.
111141

112142
### Attributes
113143

@@ -211,7 +241,7 @@ This function takes two parameters:
211241
let hi : unit -> unit = import "say_hello" "hello"
212242
213243
hi()
214-
// Generates:
244+
// Generates:
215245
// from typing import Callable
216246
// from hello import say_hello as say_hello_1
217247
// say_hello: Callable[[], None] = say_hello_1
@@ -231,7 +261,7 @@ let hi : unit -> unit = importAll "./hello.js"
231261

232262
#### `importMember`
233263

234-
`importMember` is used to import a specific member from a JavaScript module, the name is based on the name of the F# value.
264+
`importMember` is used to import a specific member from a Python module, the name is based on the name of the F# value.
235265

236266
```fs
237267
let say_hello : unit -> unit = importMember "hello"
@@ -247,6 +277,16 @@ let sayHello : unit -> unit = importMember "hello"
247277
// say_hello: Callable[[], None] = say_hello_1
248278
```
249279

280+
#### `importSideEffects`
281+
282+
`importSideEffects` is used when you want to import a Python module for its side effects only.
283+
284+
```fs
285+
importSideEffects "some_module"
286+
// Generates:
287+
// import some_module
288+
```
289+
250290
## Emit, when F# is not enough
251291

252292
Emit is a features offered by Fable, that allows you to write Python code directly in F#.
@@ -265,7 +305,7 @@ When using `emit`, you can use placeholders like `$0`, `$1`, `$2`, ... to refere
265305

266306
### `[<Emit("...")>]`
267307

268-
You can use the `Emit` attribute to decorate function, methods,
308+
You can use the `Emit` attribute to decorate function, methods,
269309

270310
```fs
271311
[<Emit("$0 + $1")>]
@@ -343,11 +383,11 @@ Deconstruct a tuple of arguments and generate a Python statement.
343383
```fs
344384
open Fable.Core.PyInterop
345385
346-
let add (a : int) (b : int) : int =
386+
let add (a : int) (b : int) : int =
347387
emitStatement (a, b) "return $0 + $1;"
348388
349389
let repeatHello (count : int) : unit =
350-
emitStatement
390+
emitStatement
351391
count
352392
"""cond = count;
353393
while cond > 0:
@@ -373,10 +413,10 @@ def repeat_hello(count: int) -> None:
373413
### `emitExpr` vs `emitStatement`
374414

375415
```fs
376-
let add1 (a : int) (b : int) =
416+
let add1 (a : int) (b : int) =
377417
emitExpr (a, b) "$0 + $1"
378418
379-
let add2 (a : int) (b : int) =
419+
let add2 (a : int) (b : int) =
380420
emitStatement (a, b) "$0 + $1"
381421
```
382422

@@ -395,14 +435,104 @@ def add2(a: int, b: int) -> Any:
395435

396436
Note how `return` has been added to `add1` and not to `add2`. In this situation if you use `emitStatement`, you need to write `return $0 + $1"`
397437

438+
### `Py.python`
439+
440+
<p class="tag is-info is-medium">
441+
Added in v5.0.0
442+
</p>
443+
444+
`Py.python` allows you to embed literal Python code directly in F#.
445+
446+
```fs
447+
open Fable.Core
448+
449+
let add a b = Py.python $"""return {a+b}"""
450+
```
451+
452+
generates:
453+
454+
```py
455+
def add(a, b):
456+
return a + b
457+
```
458+
459+
`Py.python` executes as statements, so use `return` keyword to return values.
460+
461+
## Python Decorators
462+
463+
<p class="tag is-info is-medium">
464+
Added in v5.0.0
465+
</p>
466+
467+
`Py.Decorator` allows you to apply Python decorators to classes and functions.
468+
469+
```fs
470+
open Fable.Core
471+
472+
[<Py.Decorator("dataclasses.dataclass")>]
473+
type User =
474+
{ Name: string
475+
Age: int }
476+
```
477+
478+
generates:
479+
480+
```py
481+
@dataclasses.dataclass
482+
class User:
483+
name: str
484+
age: int
485+
```
486+
487+
You can also pass parameters to decorators:
488+
489+
```fs
490+
[<Py.Decorator("functools.lru_cache", "maxsize=128")>]
491+
let expensiveFunction x = x * 2
492+
```
493+
494+
generates:
495+
496+
```py
497+
@functools.lru_cache(maxsize=128)
498+
def expensive_function(x):
499+
return x * 2
500+
```
501+
502+
## Class Attributes
503+
504+
<p class="tag is-info is-medium">
505+
Added in v5.0.0
506+
</p>
507+
508+
`Py.ClassAttributes` controls how class members are generated in Python.
509+
510+
```fs
511+
open Fable.Core
512+
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
517+
```
518+
519+
generates:
520+
521+
```py
522+
class Config:
523+
name: str = "default"
524+
port: int = 8080
525+
```
526+
527+
Without `ClassAttributes`, members would be generated as properties with instance backing.
398528

399529
## `[<Erase>]`
400530

401531
### Erased unions
402532

403533
You can decode a type with `[<Erase>]` to tells fable to not emit code for that type.
404534

405-
This is useful for creating DSL for examples or when trying to represent a virtual type
535+
This is useful for creating DSL for examples or when trying to represent a virtual type
406536
for which you don't want to impact the size of the generated code.
407537

408538
```fs
@@ -472,13 +602,13 @@ let test(arg: U3<string, int, float[]>) =
472602
// to_console(printf("An int %i"))(arg)
473603
// elif is_array_like(arg):
474604
// to_console(printf("An array with sum %f"))(arg)
475-
// else:
605+
// else:
476606
// to_console(printf("A string %s"))(arg)
477607
```
478608

479609
### Erased types
480610

481-
Decoring a type with `[<Erase>]` allows you to instruct Fable to not generate any code for that type. This is useful when you want to use a type from a Python library that you don't want to generate bindings for.
611+
Decorating a type with `[<Erase>]` allows you to instruct Fable to not generate any code for that type. This is useful when you want to use a type from a Python library that you don't want to generate bindings for.
482612

483613
```fs
484614
open Fable.Core
@@ -555,7 +685,7 @@ The generated code is much smaller and doesn't include any reflection informatio
555685

556686
## `[<StringEnum>]`
557687

558-
:::info
688+
:::info
559689
These union types must not have any data fields as they will be compiled to a string matching the name of the union case.
560690
:::
561691

@@ -585,7 +715,7 @@ on_event("click", ignore)
585715

586716
### `CaseRules`
587717

588-
`StringEnum` accept a parameters allowing you to control the casing used to conver the union case name to a string.
718+
`StringEnum` accept a parameters allowing you to control the casing used to convert the union case name to a string.
589719

590720
- `CaseRules.None`: `MouseOver` becomes `MouseOver`
591721
- `CaseRules.LowerFirst`: `MouseOver` becomes `mouseOver`
@@ -681,7 +811,7 @@ You then need to set each field manually.
681811
open Fable.Core
682812
open Fable.Core.PyInterop
683813
684-
type IUser =
814+
type IUser =
685815
abstract Name : string with get, set
686816
abstract Age : int with get, set
687817
@@ -801,7 +931,7 @@ class MinStack:
801931
Added in v3.2.0
802932
</p>
803933

804-
If you are not planning to use an interface to interact with Python and want to have overloaded members, you can decorate the interface declaration with the `Mangle` attribute.
934+
If you are not planning to use an interface to interact with Python and want to have overloaded members, you can decorate the interface declaration with the `Mangle` attribute.
805935

806936
> Interfaces coming from .NET BCL (like System.Collections.IEnumerator) are mangled by default.
807937
@@ -814,10 +944,10 @@ type IRenderer =
814944
815945
type Renderer() =
816946
interface IRenderer with
817-
member this.Render() =
947+
member this.Render() =
818948
failwith "Not implemented"
819949
820-
member this.Render(indentation) =
950+
member this.Render(indentation) =
821951
failwith "Not implemented"
822952
```
823953

@@ -913,12 +1043,12 @@ useEffect(Func<_,_> myEffect)
9131043

9141044
## Dynamic typing, proceed with caution
9151045

916-
Dynamic typing allows you to access an object property by name
1046+
Dynamic typing allows you to access an object property by name
9171047

9181048
:::danger
919-
This feature is not type-safe and should be used with caution.
1049+
This feature is not type-safe and should be used with caution.
9201050

921-
Adocate use case for this feature is when you are prototyping or don't have the time to write a proper type-safe interop.
1051+
Advocate use case for this feature is when you are prototyping or don't have the time to write a proper type-safe interop.
9221052
:::
9231053

9241054
### Property access

0 commit comments

Comments
 (0)