diff --git a/docs/docs/python/build-and-run.md b/docs/docs/python/build-and-run.md index 1c89b71d..a343c11c 100644 --- a/docs/docs/python/build-and-run.md +++ b/docs/docs/python/build-and-run.md @@ -3,10 +3,32 @@ title: Build and Run layout: standard --- -When targetting python, you can use the output of Fable directly by running it with the python interpreter. +## Python Version + +Fable targets Python 3.12 or higher. + +Python 3.10 and 3.11 are deprecated. + +## Running Python Code + +When targeting python, you can use the output of Fable directly by running it with the python interpreter. For example: ```bash python3 Program.py ``` + +## Publishing to PyPI + +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: + +:::info +This is because the Fable library is partially written in Rust and needs to be built for all architectures +::: + +```bash +dotnet fable --lang python --fableLib fable-library +``` + +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. diff --git a/docs/docs/python/features.md b/docs/docs/python/features.md index c04858a9..a39c159f 100644 --- a/docs/docs/python/features.md +++ b/docs/docs/python/features.md @@ -18,7 +18,7 @@ Python target is in beta meaning that breaking changes can happen between minor When targetting Python, Fable will automatically convert F# camelCase names to Python snake_case names. ```fs -let addTwoNumber x y = +let addTwoNumber x y = x + y ``` @@ -29,6 +29,36 @@ def add_two_number(x: int, y: int) -> int: return x + y ``` +Records snake-case all member fields: + +

+ Added in v5.0.0-alpha +

+ +```fs +type User = { FirstName: string } +``` + +generates: + +```py +class User: + def __init__(self, first_name: str): + self.first_name = first_name +``` + +Anonymous records preserve original casing: + +```fs +let user = {| FirstName = "John" |} +``` + +generates: + +```py +user = {"FirstName": "John"} +``` + ### `nativeOnly` `nativeOnly` provide a dummy implementation that we use when writing bindings. @@ -37,7 +67,7 @@ Here is its definition: ```fs /// Alias of nativeOnly - let inline nativeOnly<'T> : 'T = + let inline nativeOnly<'T> : 'T = failwith "You've hit dummy code used for Fable bindings. This probably means you're compiling Fable code to .NET by mistake, please check." ``` @@ -80,7 +110,7 @@ There are 2 families of imports: - Attribute-based imports - Function-based imports -They archieve the same goal, but with a slightly generated code. +They achieve the same goal, but with a slightly generated code. ```fs [] @@ -107,7 +137,7 @@ say_hello: Callable[[], None] = say_hello_1 say_hello() ``` -Using the attribute-based imports is recommanded as it generates a smaller output. +Using the attribute-based imports is recommended as it generates a smaller output. ### Attributes @@ -211,7 +241,7 @@ This function takes two parameters: let hi : unit -> unit = import "say_hello" "hello" hi() -// Generates: +// Generates: // from typing import Callable // from hello import say_hello as say_hello_1 // say_hello: Callable[[], None] = say_hello_1 @@ -231,7 +261,7 @@ let hi : unit -> unit = importAll "./hello.js" #### `importMember` -`importMember` is used to import a specific member from a JavaScript module, the name is based on the name of the F# value. +`importMember` is used to import a specific member from a Python module, the name is based on the name of the F# value. ```fs let say_hello : unit -> unit = importMember "hello" @@ -247,6 +277,16 @@ let sayHello : unit -> unit = importMember "hello" // say_hello: Callable[[], None] = say_hello_1 ``` +#### `importSideEffects` + +`importSideEffects` is used when you want to import a Python module for its side effects only. + +```fs +importSideEffects "some_module" +// Generates: +// import some_module +``` + ## Emit, when F# is not enough 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 ### `[]` -You can use the `Emit` attribute to decorate function, methods, +You can use the `Emit` attribute to decorate function, methods, ```fs [] @@ -343,11 +383,11 @@ Deconstruct a tuple of arguments and generate a Python statement. ```fs open Fable.Core.PyInterop -let add (a : int) (b : int) : int = +let add (a : int) (b : int) : int = emitStatement (a, b) "return $0 + $1;" let repeatHello (count : int) : unit = - emitStatement + emitStatement count """cond = count; while cond > 0: @@ -373,10 +413,10 @@ def repeat_hello(count: int) -> None: ### `emitExpr` vs `emitStatement` ```fs -let add1 (a : int) (b : int) = +let add1 (a : int) (b : int) = emitExpr (a, b) "$0 + $1" -let add2 (a : int) (b : int) = +let add2 (a : int) (b : int) = emitStatement (a, b) "$0 + $1" ``` @@ -395,6 +435,96 @@ def add2(a: int, b: int) -> Any: 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"` +### `Py.python` + +

+ Added in v4.0.0 +

+ +`Py.python` allows you to embed literal Python code directly in F#. + +```fs +open Fable.Core + +let add a b = Py.python $"""return {a+b}""" +``` + +generates: + +```py +def add(a: int32, b: int32) -> Any: + return a + b +``` + +`Py.python` executes as statements, so use `return` keyword to return values. + +## Python Decorators + +

+ Added in v5.0.0-alpha +

+ +`Py.Decorator` allows you to apply Python decorators to classes and functions. + +```fs +open Fable.Core + +[] +type User = + { Name: string + Age: int } +``` + +generates: + +```py +@dataclasses.dataclass +class User: + name: str + age: int32 +``` + +You can also pass parameters to decorators: + +```fs +[] +let expensiveFunction x = x * 2 +``` + +generates: + +```py +@functools.lru_cache(maxsize=128) +def expensive_function(x): + return x * 2 +``` + +## Class Attributes + +

+ Added in v5.0.0-alpha +

+ +`Py.ClassAttributes` controls how class members are generated in Python. + +```fs +open Fable.Core + +[] +type Config() = + member val Name = "default" with get, set + member val Port = 8080 with get, set +``` + +generates: + +```py +class Config: + name: str = "default" + port: int = 8080 +``` + +Without `ClassAttributes`, members would be generated as properties with instance backing. ## `[]` @@ -402,7 +532,7 @@ Note how `return` has been added to `add1` and not to `add2`. In this situation You can decode a type with `[]` to tells fable to not emit code for that type. -This is useful for creating DSL for examples or when trying to represent a virtual type +This is useful for creating DSL for examples or when trying to represent a virtual type for which you don't want to impact the size of the generated code. ```fs @@ -472,13 +602,13 @@ let test(arg: U3) = // to_console(printf("An int %i"))(arg) // elif is_array_like(arg): // to_console(printf("An array with sum %f"))(arg) -// else: +// else: // to_console(printf("A string %s"))(arg) ``` ### Erased types -Decoring a type with `[]` 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. +Decorating a type with `[]` 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. ```fs open Fable.Core @@ -555,7 +685,7 @@ The generated code is much smaller and doesn't include any reflection informatio ## `[]` -:::info +:::info These union types must not have any data fields as they will be compiled to a string matching the name of the union case. ::: @@ -585,7 +715,7 @@ on_event("click", ignore) ### `CaseRules` -`StringEnum` accept a parameters allowing you to control the casing used to conver the union case name to a string. +`StringEnum` accept a parameters allowing you to control the casing used to convert the union case name to a string. - `CaseRules.None`: `MouseOver` becomes `MouseOver` - `CaseRules.LowerFirst`: `MouseOver` becomes `mouseOver` @@ -681,7 +811,7 @@ You then need to set each field manually. open Fable.Core open Fable.Core.PyInterop -type IUser = +type IUser = abstract Name : string with get, set abstract Age : int with get, set @@ -801,7 +931,7 @@ class MinStack: Added in v3.2.0

-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. +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. > Interfaces coming from .NET BCL (like System.Collections.IEnumerator) are mangled by default. @@ -814,10 +944,10 @@ type IRenderer = type Renderer() = interface IRenderer with - member this.Render() = + member this.Render() = failwith "Not implemented" - member this.Render(indentation) = + member this.Render(indentation) = failwith "Not implemented" ``` @@ -913,12 +1043,12 @@ useEffect(Func<_,_> myEffect) ## Dynamic typing, proceed with caution -Dynamic typing allows you to access an object property by name +Dynamic typing allows you to access an object property by name :::danger -This feature is not type-safe and should be used with caution. +This feature is not type-safe and should be used with caution. -Adocate use case for this feature is when you are prototyping or don't have the time to write a proper type-safe interop. +Adequate use case for this feature is when you are prototyping or don't have the time to write a proper type-safe interop. ::: ### Property access