Skip to content

Commit 85d6cac

Browse files
dbrattliMaxime Mangel
andauthored
docs: Update docs for Python in Fable v5 (#213)
* docs: Update docs for Python in Fable v5 * doc: fix typo * doc: remove -alpha from tag * Update docs/docs/python/build-and-run.md Co-authored-by: Maxime Mangel <mangel.maxime@protonmail.com> * Update docs/docs/python/build-and-run.md Co-authored-by: Maxime Mangel <mangel.maxime@protonmail.com> * Update docs/docs/python/build-and-run.md Co-authored-by: Maxime Mangel <mangel.maxime@protonmail.com> * doc: fix version tags for python features * doc: add missing types to generated code --------- Co-authored-by: Maxime Mangel <mangel.maxime@protonmail.com>
1 parent d8e87bb commit 85d6cac

File tree

2 files changed

+176
-24
lines changed

2 files changed

+176
-24
lines changed

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,32 @@ 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.
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+
If you want to publish a library to PyPI that uses Fable Python, you need to use `--fableLib` option to reference the pre-build Fable library:
25+
26+
:::info
27+
This is because the Fable library is partially written in Rust and needs to be built for all architectures
28+
:::
29+
30+
```bash
31+
dotnet fable --lang python --fableLib fable-library
32+
```
33+
34+
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
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 v4.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: int32, b: int32) -> Any:
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-alpha
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: int32
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-alpha
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+
Adequate 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)