|
3 | 3 |  |
4 | 4 | [](https://www.nuget.org/packages/Fable.Python/) |
5 | 5 |
|
6 | | -[Fable](https://github.com/fable-compiler/Fable/tree/beyond) is a |
7 | | -compiler that translates F# source files to JavaScript and Python. |
| 6 | +[Fable](https://github.com/fable-compiler/Fable) is a compiler that translates F# source files to JavaScript and Python. |
8 | 7 |
|
9 | | -This Fable Python repository is a community driven project that contains |
10 | | -the Python type bindings for Fable. The library will eventually contain |
11 | | -Python (stdlib) bindings for Fable based on Python |
12 | | -[typeshed](https://github.com/python/typeshed). It will also contain |
13 | | -type binding for many other 3rd party libraries such as Flask, MicroBit |
14 | | -and many more. Some bindings have already been added: |
| 8 | +**Fable.Python** provides Python type bindings for Fable, enabling you to write type-safe F# code that compiles to Python. This community-driven library includes bindings for the Python standard library and popular frameworks like Flask, FastAPI, and Pydantic. |
15 | 9 |
|
16 | | -- Python Standard Libray |
17 | | -- Jupyter |
18 | | -- Flask |
19 | | -- CogniteSdk |
| 10 | +## Requirements |
20 | 11 |
|
21 | | -## Version |
22 | | - |
23 | | -This library currently targets Python 3.10 or greater. Types bindings |
24 | | -for other versions of Python should not be added to this library until |
25 | | -we decide how to deal with Python version handling. |
| 12 | +- Python 3.12 or greater |
| 13 | +- .NET 8.0 or greater |
| 14 | +- [Fable](https://fable.io/) compiler |
26 | 15 |
|
27 | 16 | ## Installation |
28 | 17 |
|
29 | | -Prerequisite for compiling F# to Python using Fable: |
| 18 | +Install the Fable compiler: |
30 | 19 |
|
31 | 20 | ```sh |
32 | | -> dotnet tool install --global fable --prerelease |
33 | | -> dotnet add package Fable.Core --prerelease |
| 21 | +dotnet tool install --global fable --prerelease |
| 22 | +dotnet add package Fable.Core --prerelease |
34 | 23 | ``` |
35 | 24 |
|
36 | | -To use the `Fable.Python` library in your Fable project: |
| 25 | +Add Fable.Python to your project: |
37 | 26 |
|
38 | 27 | ```sh |
39 | | -> dotnet add package Fable.Python |
| 28 | +dotnet add package Fable.Python |
40 | 29 | ``` |
41 | 30 |
|
42 | | -## Usage |
| 31 | +## Quick Start |
43 | 32 |
|
44 | | -```fs |
| 33 | +```fsharp |
45 | 34 | open Fable.Python.Json |
46 | 35 |
|
47 | | -let object = {| A=10n; B=20n |} |
48 | | -let result = json.dumps object |
| 36 | +let data = {| name = "Alice"; age = 30 |} |
| 37 | +let jsonStr = dumps data |
49 | 38 | ``` |
50 | 39 |
|
51 | | -To compile an F# Fable project to Python run e.g: |
| 40 | +Compile to Python: |
52 | 41 |
|
53 | 42 | ```sh |
54 | | -> fable --lang Python MyProject.fsproj |
| 43 | +fable --lang Python MyProject.fsproj |
55 | 44 | ``` |
56 | 45 |
|
57 | | -For more examples see the |
58 | | -[examples](https://github.com/dbrattli/Fable.Python/tree/main/examples) folder. |
59 | | -It contains example code for using Fable Python with: |
60 | | - |
61 | | -- [Flask](https://github.com/dbrattli/Fable.Python/tree/main/examples/flask). |
62 | | - References [Feliz.ViewEngine](https://github.com/dbrattli/Feliz.ViewEngine) |
63 | | - as a nuget package. |
64 | | -- [Timeflies](https://github.com/dbrattli/Fable.Python/tree/main/examples/timeflies), |
65 | | - Cool demo using Tkinter and references |
66 | | - [FSharp.Control.AsyncRx](https://github.com/dbrattli/AsyncRx) as a nuget |
67 | | - package. |
68 | | - |
69 | | -## Libraries that uses or works with Fable Python |
70 | | - |
71 | | -- [Femto](https://github.com/Zaid-Ajaj/Femto) |
72 | | -- [AsyncRx](https://github.com/dbrattli/AsyncRx) |
73 | | -- [Fable.Aether](https://xyncro.tech/aether/) |
74 | | -- [Fable.Giraffe](https://github.com/dbrattli/Fable.Giraffe), port of Giraffe to Fable.Python |
75 | | -- [Fable.Logging](https://github.com/dbrattli/Fable.logging), logging for Fable.Python |
76 | | -- [Fable.Requests](https://github.com/Zaid-Ajaj/Fable.Requests) |
77 | | -- [Fable.Jupyter](https://github.com/fable-compiler/Fable.Jupyter), Jupyter Notebook using Fable.Python |
78 | | -- [Fable.Pyexpecto](https://github.com/Freymaurer/Fable.Pyxpecto), Fable-python equivalent for Fable.Mocha |
79 | | -- [Fable.SimpleJson.Python](https://github.com/Zaid-Ajaj/Fable.SimpleJson.Python) |
80 | | -- [Fable.Sedlex](https://github.com/thautwarm/Fable.Sedlex) |
81 | | -- [Feliz.ViewEngine](https://github.com/dbrattli/Feliz.ViewEngine) |
82 | | -- [FsToolkit.ErrorHandling](https://demystifyfp.gitbook.io/fstoolkit-errorhandling/) |
83 | | -- [TypedCssClasses](https://github.com/zanaptak/TypedCssClasses) |
84 | | -- [Typed-BNF](https://github.com/thautwarm/Typed-BNF#readme) |
85 | | -- [Zanaptak.TypedCssClasses](https://github.com/zanaptak/TypedCssClasses) |
86 | | - |
87 | | -## Uv |
88 | | - |
89 | | -Fable.Python uses [Uv](https://docs.astral.sh/uv/) for package and |
90 | | -dependency management. To handle dependencies when adding Fable Python |
91 | | -compatible NuGet packages, you should use |
92 | | -[Femto](https://github.com/Zaid-Ajaj/Femto). |
93 | | - |
94 | | -## Development |
| 46 | +## Available Bindings |
95 | 47 |
|
96 | | -This project uses [just](https://github.com/casey/just) as a command runner. |
| 48 | +### Python Standard Library |
97 | 49 |
|
98 | | -```sh |
99 | | -# Install just (macOS) |
100 | | -> brew install just |
| 50 | +| Module | Description | |
| 51 | +| ----------------------- | ------------------------------------------- | |
| 52 | +| `Fable.Python.Builtins` | Built-in functions (open, print, len, etc.) | |
| 53 | +| `Fable.Python.Json` | JSON serialization with Fable type support | |
| 54 | +| `Fable.Python.Os` | Operating system interfaces | |
| 55 | +| `Fable.Python.Sys` | System-specific parameters | |
| 56 | +| `Fable.Python.Math` | Mathematical functions | |
| 57 | +| `Fable.Python.Random` | Random number generation | |
| 58 | +| `Fable.Python.Logging` | Logging facilities | |
| 59 | +| `Fable.Python.Time` | Time-related functions | |
| 60 | +| `Fable.Python.String` | String operations | |
| 61 | +| `Fable.Python.Base64` | Base64 encoding/decoding | |
| 62 | +| `Fable.Python.Queue` | Queue data structures | |
| 63 | +| `Fable.Python.Ast` | Abstract Syntax Tree | |
| 64 | +| `Fable.Python.AsyncIO` | Async programming (Events, Futures, Tasks) | |
| 65 | +| `Fable.Python.TkInter` | GUI toolkit | |
101 | 66 |
|
102 | | -# Show available commands |
103 | | -> just |
| 67 | +### Web Frameworks |
104 | 68 |
|
105 | | -# Full setup (restore .NET and Python dependencies) |
106 | | -> just setup |
| 69 | +| Package | Description | |
| 70 | +| ----------------------- | ----------------------------------- | |
| 71 | +| `Fable.Python.Flask` | Flask web framework | |
| 72 | +| `Fable.Python.FastAPI` | FastAPI with automatic OpenAPI docs | |
| 73 | +| `Fable.Python.Pydantic` | Data validation and settings | |
107 | 74 |
|
108 | | -# Build F# to Python |
109 | | -> just build |
| 75 | +## JSON Serialization |
110 | 76 |
|
111 | | -# Run all tests (native .NET and Python) |
112 | | -> just test |
| 77 | +Fable types (like `Int32`, F# records, unions) need special handling for JSON serialization. Use `Fable.Python.Json.dumps`: |
113 | 78 |
|
114 | | -# Format code |
115 | | -> just format |
| 79 | +```fsharp |
| 80 | +open Fable.Python.Json |
116 | 81 |
|
117 | | -# Create NuGet package |
118 | | -> just pack |
| 82 | +type User = { Id: int; Name: string } |
| 83 | +let user = { Id = 1; Name = "Bob" } |
| 84 | +let json = dumps user // {"Id": 1, "Name": "Bob"} |
119 | 85 | ``` |
120 | 86 |
|
121 | | -## Contributing |
| 87 | +See [JSON.md](JSON.md) for detailed documentation on serialization patterns. |
122 | 88 |
|
123 | | -This project is community driven. If the type binding you are looking |
124 | | -for is currently missing, then you need to add them to the relevant |
125 | | -files (or add new ones). Open a |
126 | | -[PR](https://github.com/dbrattli/Fable.Python/pull/3/files) to get them |
127 | | -included. |
| 89 | +## Web Framework Examples |
128 | 90 |
|
129 | | -### Commit Convention |
| 91 | +### FastAPI |
130 | 92 |
|
131 | | -This project uses [Conventional Commits](https://www.conventionalcommits.org/) |
132 | | -and [release-please](https://github.com/googleapis/release-please) for automated |
133 | | -releases. PR titles must follow the format: |
| 93 | +```fsharp |
| 94 | +open Fable.Python.FastAPI |
| 95 | +open Fable.Python.Pydantic |
134 | 96 |
|
| 97 | +[<Py.ClassAttributes(style = Py.ClassAttributeStyle.Attributes, init = false)>] |
| 98 | +type UserResponse(Id: int, Name: string) = |
| 99 | + inherit BaseModel() |
| 100 | + member val Id: int = Id with get, set |
| 101 | + member val Name: string = Name with get, set |
| 102 | +
|
| 103 | +[<APIClass>] |
| 104 | +type API() = |
| 105 | + [<Get("/users/{user_id}")>] |
| 106 | + static member get_user(user_id: int) : UserResponse = |
| 107 | + UserResponse(Id = user_id, Name = "Alice") |
135 | 108 | ``` |
136 | | -type: description |
| 109 | + |
| 110 | +### Flask |
| 111 | + |
| 112 | +```fsharp |
| 113 | +open Fable.Python.Flask |
| 114 | +open Fable.Python.Json |
| 115 | +
|
| 116 | +[<APIClass>] |
| 117 | +type Routes() = |
| 118 | + [<Get("/api/hello")>] |
| 119 | + static member hello() : string = |
| 120 | + dumps {| message = "Hello, World!" |} |
137 | 121 | ``` |
138 | 122 |
|
139 | | -Where `type` is one of: |
140 | | -- `feat` - New features (bumps minor version) |
141 | | -- `fix` - Bug fixes (bumps patch version) |
142 | | -- `docs` - Documentation changes |
143 | | -- `chore` - Maintenance tasks |
144 | | -- `refactor` - Code refactoring |
145 | | -- `test` - Adding or updating tests |
146 | | -- `ci` - CI/CD changes |
147 | | -- `build` - Build system changes |
148 | | -- `perf` - Performance improvements |
| 123 | +## Examples |
149 | 124 |
|
150 | | -Breaking changes should include `!` after the type (e.g., `feat!: breaking change`) |
151 | | -and will bump the major version. |
| 125 | +The [examples](examples/) directory contains working applications: |
152 | 126 |
|
153 | | -The `src/stdlib` directory contains type bindings for modules in the |
154 | | -Python 3 standard library. We also accept type bindings for 3rd party |
155 | | -libraries as long as: |
| 127 | +| Example | Description | |
| 128 | +| ------------------------------------------ | ---------------------------------------------- | |
| 129 | +| [fastapi](examples/fastapi/) | REST API with Pydantic models and Swagger docs | |
| 130 | +| [flask](examples/flask/) | Web app with Feliz.ViewEngine HTML rendering | |
| 131 | +| [django](examples/django/) | Full Django project | |
| 132 | +| [django-minimal](examples/django-minimal/) | Single-file Django app | |
| 133 | +| [pydantic](examples/pydantic/) | Pydantic model examples | |
| 134 | +| [timeflies](examples/timeflies/) | Tkinter GUI with AsyncRx | |
156 | 135 |
|
157 | | -- the package is publicly available on the [Python Package Index](https://pypi.org/); |
158 | | -- the package supports any Python version supported by Fable Python; and |
159 | | -- the package does not ship with its own stubs or type annotations |
| 136 | +Run an example: |
160 | 137 |
|
161 | | -There's not much Python specific documentation yet, but the process of |
162 | | -adding type bindings for Python is similar to JS: |
| 138 | +```sh |
| 139 | +just example-fastapi # FastAPI with auto-reload |
| 140 | +just example-flask # Flask web app |
| 141 | +just example-timeflies # Tkinter desktop app |
| 142 | +``` |
163 | 143 |
|
164 | | -- <https://fable.io/docs/communicate/js-from-fable.html> |
165 | | -- <https://medium.com/@zaid.naom/f-interop-with-javascript-in-fable-the-complete-guide-ccc5b896a59f> |
| 144 | +## Development |
166 | 145 |
|
167 | | -## Differences from JS |
| 146 | +This project uses [just](https://github.com/casey/just) as a command runner and [uv](https://docs.astral.sh/uv/) for Python package management. |
168 | 147 |
|
169 | | -Note that import all is different from JS. E.g: |
| 148 | +### Setup |
170 | 149 |
|
171 | | -```fs |
172 | | -[<ImportAll("flask")>] |
173 | | -let flask: IExports = nativeOnly |
| 150 | +```sh |
| 151 | +# Install just (macOS) |
| 152 | +brew install just |
| 153 | + |
| 154 | +# Full setup (restore .NET and Python dependencies) |
| 155 | +just setup |
| 156 | +``` |
| 157 | + |
| 158 | +### Commands |
| 159 | + |
| 160 | +```sh |
| 161 | +just # Show all available commands |
| 162 | +just build # Build F# to Python |
| 163 | +just test # Run all tests (native .NET and Python) |
| 164 | +just test-python # Run only Python tests |
| 165 | +just format # Format code with Fantomas |
| 166 | +just pack # Create NuGet package |
| 167 | +just clean # Clean build artifacts |
174 | 168 | ``` |
175 | 169 |
|
176 | | -This will generate `import flask` and not a wildcard import `from flask import |
177 | | -*`. The latter version is discoraged anyways. |
| 170 | +### Project Structure |
| 171 | + |
| 172 | +```txt |
| 173 | +src/ |
| 174 | +├── stdlib/ # Python standard library bindings |
| 175 | +├── flask/ # Flask bindings |
| 176 | +├── fastapi/ # FastAPI bindings |
| 177 | +├── pydantic/ # Pydantic bindings |
| 178 | +└── jupyter/ # Jupyter bindings |
| 179 | +test/ # Test suite |
| 180 | +examples/ # Example applications |
| 181 | +build/ # Generated Python output (gitignored) |
| 182 | +``` |
| 183 | + |
| 184 | +## Compatible Libraries |
| 185 | + |
| 186 | +These libraries work with Fable.Python: |
| 187 | + |
| 188 | +- [AsyncRx](https://github.com/dbrattli/AsyncRx) - Reactive programming |
| 189 | +- [Fable.Giraffe](https://github.com/dbrattli/Fable.Giraffe) - Giraffe port |
| 190 | +- [Fable.Logging](https://github.com/dbrattli/Fable.logging) - Logging |
| 191 | +- [Fable.Requests](https://github.com/Zaid-Ajaj/Fable.Requests) - HTTP requests |
| 192 | +- [Fable.Jupyter](https://github.com/fable-compiler/Fable.Jupyter) - Jupyter notebooks |
| 193 | +- [Fable.Pyexpecto](https://github.com/Freymaurer/Fable.Pyxpecto) - Testing |
| 194 | +- [Fable.SimpleJson.Python](https://github.com/Zaid-Ajaj/Fable.SimpleJson.Python) - JSON parsing |
| 195 | +- [Fable.Sedlex](https://github.com/thautwarm/Fable.Sedlex) - Lexer generator |
| 196 | +- [Feliz.ViewEngine](https://github.com/dbrattli/Feliz.ViewEngine) - HTML rendering |
| 197 | +- [Femto](https://github.com/Zaid-Ajaj/Femto) - Package management |
| 198 | +- [FsToolkit.ErrorHandling](https://demystifyfp.gitbook.io/fstoolkit-errorhandling/) - Error handling |
| 199 | +- [TypedCssClasses](https://github.com/zanaptak/TypedCssClasses) - Type-safe CSS |
| 200 | + |
| 201 | +## Contributing |
| 202 | + |
| 203 | +Contributions are welcome! If a type binding you need is missing, open a [PR](https://github.com/dbrattli/Fable.Python/pulls) to add it. |
| 204 | + |
| 205 | +### Commit Convention |
| 206 | + |
| 207 | +This project uses [Conventional Commits](https://www.conventionalcommits.org/) for automated releases: |
| 208 | + |
| 209 | +| Type | Description | |
| 210 | +| ---------- | ---------------------------------- | |
| 211 | +| `feat` | New features (bumps minor version) | |
| 212 | +| `fix` | Bug fixes (bumps patch version) | |
| 213 | +| `docs` | Documentation changes | |
| 214 | +| `chore` | Maintenance tasks | |
| 215 | +| `refactor` | Code refactoring | |
| 216 | +| `test` | Tests | |
| 217 | + |
| 218 | +Breaking changes use `!` (e.g., `feat!: breaking change`). |
178 | 219 |
|
179 | | -## Auto-generation |
| 220 | +### Adding Bindings |
180 | 221 |
|
181 | | -Parts of this library could benefit from code-generation based on the type |
182 | | -annotations in Python [typeshed](https://github.com/python/typeshed) similar to |
183 | | -[ts2fable](https://github.com/fable-compiler/ts2fable). Even so we should keep |
184 | | -this library manually updated based on PRs to ensure the quality of the code. |
| 222 | +The `src/stdlib/` directory contains Python standard library bindings. Third-party library bindings are accepted if: |
185 | 223 |
|
186 | | -Current plan: |
| 224 | +- The package is publicly available on [PyPI](https://pypi.org/) |
| 225 | +- The package supports Python 3.12+ |
| 226 | +- The package doesn't ship with its own type stubs |
187 | 227 |
|
188 | | -1. Add bindings for Python `ast` module (in progress) |
189 | | -2. Use `ast` module to parse Python typeshed annotations |
190 | | -3. Generate F# bindings |
| 228 | +For guidance on creating bindings, see: |
191 | 229 |
|
192 | | -## Road-map |
| 230 | +- [Fable JS Interop](https://fable.io/docs/communicate/js-from-fable.html) (patterns apply to Python) |
| 231 | +- [F# Interop Guide](https://medium.com/@zaid.naom/f-interop-with-javascript-in-fable-the-complete-guide-ccc5b896a59f) |
193 | 232 |
|
194 | | -- Use a dedicated List.fs for Python. List.fs currently depends on |
195 | | - Array.fs that is not an efficient list implementation for Python. |
| 233 | +### Import Pattern |
| 234 | + |
| 235 | +Note that `ImportAll` generates a module import: |
| 236 | + |
| 237 | +```fsharp |
| 238 | +[<ImportAll("flask")>] |
| 239 | +let flask: IExports = nativeOnly |
| 240 | +``` |
196 | 241 |
|
197 | | -- Compile Fable.Library as a published library (done) |
| 242 | +This generates `import flask`, not `from flask import *`. |
198 | 243 |
|
199 | | -- Use uv for Python references to Fable modules (done) |
| 244 | +## License |
200 | 245 |
|
201 | | -- Update docs |
| 246 | +MIT |
0 commit comments